Optimizing Web Vitals: INP Performance
Google officially replaced First Input Delay (FID) with Interaction to Next Paint (INP) as a Core Web Vitals metric. While FID measured only the delay before the browser began processing the first user interaction, INP measures the overall latency of all user interactions (clicks, taps, and keypresses) throughout the entire lifespan of a web page.
A bad INP score (>200 milliseconds) causes web application interfaces to feel laggy, unresponsive, or frozen when users click buttons or open dropdown menus. Optimizing INP requires diagnosing main-thread blocking JavaScript tasks and refactoring execution flow using modern Web APIs. This guide details INP measurement breakdown, long task decomposition with scheduler.yield(), Web Workers offloading, and React 19 transition optimization.
Mental Model: First Input Delay (FID) vs Interaction to Next Paint (INP)
Legacy FID measured only the time between a user's initial click and when the browser's main thread became available to start executing event listeners. FID completely ignored the time spent executing event handler logic or rendering visual UI updates to the screen.
Interaction to Next Paint (INP) measures total end-to-end interaction latency: $$\text{INP Latency} = \text{Input Delay} + \text{Processing Duration} + \text{Presentation Delay}$$
INP evaluates the single worst interaction latency (or 98th percentile for pages with many interactions). An INP under 200ms is considered 'Good', 200ms–500ms 'Needs Improvement', and >500ms 'Poor'. For Core Web Vitals optimization, review optimizing frontend performance core web vitals and observable state management react.
Quick reference
- INP measures the end-to-end latency of all user clicks, taps, and keyboard inputs on a page.
- A good INP score requires visual paint feedback in under 200 milliseconds.
- INP comprises 3 distinct sub-phases: Input Delay, Processing Duration, and Presentation Delay.
- Evaluates the 98th percentile interaction duration across a user's browsing session.
- Directly impacts Google Search ranking signals and user engagement metrics.
Remember this
Optimize all 3 phases of INP interaction latency to ensure visual feedback renders in under 200ms.
Anatomy of a Slow Interaction: Input Delay, Processing Time, & Presentation Delay
Diagnosing slow INP requires identifying which sub-phase is consuming the main thread:
1. Input Delay: Time spent waiting for previous long tasks running on the main thread to complete before the browser can trigger the interaction's event listener.
2. Processing Duration: Time spent executing JavaScript code inside event listeners (onClick, onKeyDown). Heavy synchronous data filtering or DOM recalculations block this phase.
3. Presentation Delay: Time required by the browser compositor engine to recalculate CSS layout (reflow), paint pixels (repaint), and composite layers to render the updated frame to the GPU.
Quick reference
- Input Delay is caused by background main-thread tasks blocking new user interaction events.
- Processing Duration measures synchronous JavaScript execution time inside click handlers.
- Presentation Delay occurs when complex DOM layout reflows block GPU frame composition.
- Identifying the specific bottleneck phase guides whether to yield thread time or simplify DOM style calculations.
- Chrome DevTools Performance panel breaks down interactions into these 3 precise color-coded phases.
Remember this
Audit Chrome DevTools Performance traces to isolate whether Input Delay, Processing, or Presentation causes high INP.
Breaking Long Tasks with scheduler.yield(), requestAnimationFrame(), & Web Workers
Any JavaScript task executing for >50ms is classified as a Long Task. Long tasks block the main thread and degrade INP.
### 1. Yielding the Main Thread with scheduler.yield()
Modern browsers support the prioritize-yielding API scheduler.yield(), which yields control back to the browser compositor to draw a paint frame before continuing task execution:
1async function handleFilterButtonClick(items) {2 // 1. Immediately update UI spinner3 showSpinner();4 await scheduler.yield(); // Yield to main thread for fast paint!5 6 // 2. Heavy filtering logic in chunked batches7 const results = processHeavyDataset(items);8 renderResults(results);9}### 2. Offloading Compute to Web Workers Move non-DOM computational tasks (JSON parsing, data transformation, state diffing) off the main thread entirely into a background Web Worker thread.
Quick reference
- Long tasks (>50ms) freeze main-thread responsiveness and cause high INP scores.
- scheduler.yield() yields main-thread control to allow the browser to paint user UI updates.
- Fallback yielding via setTimeout(..., 0) or requestAnimationFrame yields to macro-task queues.
- Web Workers execute heavy calculations on background OS threads without blocking UI rendering.
- React 19 useTransition hook marks non-urgent state updates to avoid blocking urgent user typing input.
Remember this
Use scheduler.yield() and Web Workers to break up long tasks and yield main-thread rendering prioritization.
Measuring INP with PerformanceObserver, Chrome DevTools, & Web-Vitals Library
Capturing real-user INP metrics (RUM - Real User Monitoring) is essential for validating performance improvements in production:
1import { onINP } from 'web-vitals';2 3onINP((metric) => {4 // Send INP metric & interaction target element to analytics backend5 const body = JSON.stringify({6 value: metric.value, // e.g. 142ms7 rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'8 element: metric.entries[0]?.target?.tagName,9 });10 navigator.sendBeacon('/api/analytics/vitals', body);11});In local development, use Chrome DevTools Performance Panel -> Interactions track to inspect interaction frame timings and trace CPU execution stack traces.
Quick reference
- web-vitals JavaScript library captures INP metric events across real production user sessions.
- navigator.sendBeacon reliably transmits performance payloads to backend telemetry services on page unload.
- PerformanceObserver API listens for 'first-input' and 'event' PerformanceEventTiming entries natively.
- Chrome DevTools Interactions track highlights long interactions and long-animation-frame (LoAF) scripts.
- Continuous RUM monitoring alerts engineering teams when new feature deploys degrade INP metrics.
Remember this
Instrument the web-vitals library to monitor real-user INP metrics and catch main-thread regressions.
Key takeaway
To test INP optimizations, profile a button click in Chrome DevTools Performance Panel (CPU 4x Slowdown). Verify that main-thread long tasks are broken into sub-50ms chunks.
Related Articles
Explore this topic