Interaction to Next Paint (INP)

TL;DR / INP measures the latency from user interaction to the next visual update across all interactions in a session, using the worst-case value (at the 98th percentile) as the page's responsiveness score.

How It Works

 ┌────────────┐        ┌──────────┐        ┌────────────┐        ┌──────────────┐        ┌──────────┐
 │    User    │        │  Input   │        │ Processing │        │ Presentation │        │   Next   │
 │ Interacts  │───────→│  Delay   │───────→│    Time    │───────→│    Delay     │───────→│  Paint   │
 └────────────┘        └──────────┘        └────────────┘        └──────────────┘        └──────────┘

                       |------------------------- INP -------------------------|
                           worst interaction across full session (p98)

Edit diagram

Interaction to Next Paint replaced First Input Delay as Google's responsiveness Core Web Vital in March 2024. Where FID measured only the input delay of the first interaction, INP captures the full duration -- input delay, processing time, and presentation delay -- across every interaction in the page session, reporting the worst (or near-worst) one.

An interaction in INP's definition is a logical user action that may consist of multiple events. A keyboard interaction includes keydown, keypress, and keyup. A tap includes pointerdown, pointerup, and click. INP groups these events by interactionId and takes the longest event's duration as the interaction's latency. This grouping prevents double-counting -- a slow click handler does not create two separate high-latency entries just because pointerdown also fired.

The three latency components are distinct optimization targets. Input delay is time the event waits in the queue while a long task blocks the main thread (same as FID, but for every interaction). Processing time is the duration of your event handlers (all listeners for that event, synchronous code). Presentation delay is time between handler completion and the browser producing the next frame (style recalculation, layout, paint, compositing).

INP selects the highest interaction latency as the page's score, with one exception: for pages with many interactions (50+), it uses the 98th percentile rather than the absolute maximum. This avoids penalizing pages for single outlier interactions caused by unrelated factors. For pages with fewer than 50 interactions, it is the literal worst interaction.

Google's thresholds: Good is under 200ms, Needs Improvement is 200-500ms, Poor is above 500ms. These are measured at the 75th percentile of page loads in CrUX field data.

Measuring INP requires observing type: 'event' entries with durationThreshold: 16 (the minimum threshold). The entry provides startTime, processingStart, processingEnd, duration, and interactionId. The duration property is already rounded to the nearest 8ms (for security reasons, preventing timing attacks). Group entries by interactionId, take the max duration per group, then select the worst (or p98) across groups. The web-vitals library handles this aggregation correctly.

Optimizing input delay means keeping the main thread unblocked. Break long tasks with scheduler.yield(). Defer non-critical work. Use Web Workers for computation. Ensure third-party scripts do not schedule long-running tasks during interaction-heavy phases.

Optimizing processing time means making event handlers fast. Debounce expensive operations triggered by rapid-fire events. Avoid synchronous layout reads (which trigger forced reflow) inside handlers. Move DOM mutations to requestAnimationFrame to batch them. Use content-visibility: auto to reduce layout scope.

Optimizing presentation delay targets rendering cost. Reduce DOM size (fewer elements means faster layout/paint). Avoid animating layout properties (width, height, top, left) -- use transform and opacity which run on the compositor. Use will-change to promote elements to their own compositing layer. CSS containment (contain: layout style paint) limits the scope of rendering recalculations.

Specific interaction patterns that produce poor INP: accordion/disclosure widgets that insert large DOM subtrees, virtual scroll implementations that recalculate on every scroll interaction, form inputs with synchronous validation against large datasets, and client-side filtering/sorting of large lists triggered by input events.

The DevTools Performance panel's "Interactions" track visualizes each interaction with its three phases, making it the primary debugging tool. The "INP" badge appears on the worst interaction. Selecting an interaction highlights the associated main-thread activity in the flame chart.

Gotchas

  • INP includes presentation delay, unlike FID -- a fast event handler followed by an expensive layout/paint still produces a poor INP score; optimizing handler code alone is insufficient
  • The 200ms threshold is significantly harder to meet than FID's 100ms -- INP measures end-to-end latency for the worst interaction, while FID measured only input delay for the first interaction
  • Hover and scroll events do not count as interactions -- INP only tracks discrete events (click, tap, drag, keyboard); scroll jank is not captured by this metric
  • interactionId grouping is essential for correct measurement -- a single tap fires pointerdown, pointerup, and click; counting each separately inflates the interaction count and misidentifies the p98 value
  • Duration values are quantized to 8ms -- the Event Timing API rounds durations for security; do not expect sub-millisecond precision in INP measurements