Understanding and Improving Interaction to Next Paint (INP)

Redaktion ·

INP is the metric that measures how quickly your page responds to what users actually do — click, type, tap on mobile. Since March 12, 2024, Interaction to Next Paint has been an official Core Web Vital, replacing First Input Delay (FID). That is more than a rename: FID was an almost flattering metric, INP is honest.

What INP Actually Measures — and Why It Is Tougher Than FID

INP measures the latency from a user interaction to the moment the browser can paint the next frame. Every one of these latencies contains three phases:

Input delay. The time between the interaction and the start of the event handler. If the main thread is busy with a long task, your click has to wait — the handler simply can’t run yet.

Processing duration. The time all associated event handlers need to run to completion. This is the code you wrote yourself: the onClick, the state update, the validation.

Presentation delay. The time after the handler until the browser actually renders the result to the screen — style recalc, layout, paint.

The decisive difference from FID: FID measured only the delay of the first interaction, and only its input delay — not the processing, not the rendering. A page could post a top FID score and still feel sluggish on every other click. INP observes all interactions across the entire page lifecycle and essentially takes the worst value. So if you have a single janky button, it now shows up in the score.

The Thresholds — and How the Value Is Calculated

Google rates INP in three tiers, measured at the 75th percentile of all page views (per web.dev, as of June 2026):

  • good: ≤ 200 ms
  • needs improvement: 201–500 ms
  • poor: > 500 ms

It measures clicks, taps on touchscreens, and key presses. Scrolling, hovering, and zooming explicitly don’t count — those aren’t discrete interactions with a handler response.

When summarizing across the page lifecycle: if a page has fewer than 50 interactions, the worst one simply counts. With more interactions, the method ignores one outlier per 50 interactions so a single hiccup doesn’t tip the whole picture. In practice that means you can’t afford a permanently slow interaction path, but a rare slip is forgiven.

The Main Cause: Long Tasks Blocking the Main Thread

Almost every INP problem has the same root — JavaScript that holds the main thread too long at a stretch. The browser can do only one thing at a time per thread. If a task runs longer than 50 ms (a long task), no event handler can start and no frame can be painted during that window. Every click that falls into it accumulates input delay.

Typical sources of long tasks:

  • large, uninterrupted JavaScript bundles that parse and execute everything at once on load
  • heavy event handlers that render, sort, or filter synchronously
  • third-party scripts (tag managers, chat widgets, A/B tools) running unchecked on the main thread
  • expensive layout work in the presentation phase

The related topic of render-blocking resources is more about the initial build — INP, by contrast, hurts continuously, on every interaction after load.

The Levers — Break Work Up Instead of Deferring It

Split long tasks. Break long synchronous functions into smaller chunks and hand control back to the main thread in between. The modern tool for this is scheduler.yield() — a deliberate “take a breath” after which your own task resumes with priority. As a fallback, an await on a setTimeout promise works, or requestIdleCallback for non-critical work. In between, the browser can respond to waiting clicks.

Decouple work from the interaction. Separate the visible response from the expensive work. On click, first render only the UI feedback (button state, spinner), then do the actual computation after a yield or in the next frame. The user sees immediately that something is happening — that feels faster, even if the result takes just as long.

Less and lighter JavaScript. Code-split so not everything parses on the first render. Remove unused code. Question third-party scripts and load them deferred. Every line of JS that doesn’t run can’t create a long task either.

Web workers for heavy computation. Anything that doesn’t need DOM access — parsing large data sets, sorting, cryptography — belongs in a web worker. It runs on its own thread, leaving the main thread free for interactions.

How It Differs From LCP and CLS

The three Core Web Vitals measure different things, and they can even compete:

  • LCP measures load speed — when the largest element is visible. A loading problem.
  • INP measures responsiveness after load — how smoothly the page reacts to input. A runtime problem.
  • CLS measures visual stability — whether content shifts unexpectedly. A layout problem.

The conflict: improving LCP by aggressively preloading lots of JavaScript can worsen INP, because more script work burdens the main thread. Good performance means looking at all three together — not optimizing one metric against another. If you want the bigger picture of rendering and indexing, you’ll find it in the overview of technical SEO.

FAQ

Since when has INP been a Core Web Vital? Since March 12, 2024. On that day INP replaced First Input Delay (FID) as the third Core Web Vital. FID no longer exists as a diagnostic metric in the official set.

What is a good INP value? An INP of 200 milliseconds or less is considered good, measured at the 75th percentile of page views. 201 to 500 ms needs improvement, above 500 ms is poor.

Why is INP stricter than FID? FID measured only the input delay of the very first interaction. INP measures all interactions across the whole page lifecycle and also includes processing and rendering time. A single slow button now stands out.

What is the most common cause of poor INP? Long JavaScript tasks that block the main thread. As long as a task runs, no event handler can start and no frame can be painted — every click in that window becomes slow.

Does scheduler.yield() really help? Yes. scheduler.yield() hands control back to the browser in between so waiting interactions get their turn, then resumes your task with priority. Where it isn’t available yet, splitting via setTimeout or web workers helps.