Writing

Article

INP: what replaced FID, and why long tasks are the thing to hunt

Interaction to Next Paint replaced First Input Delay as a Core Web Vital in March 2024. What it measures, why it is harder to pass, and how to find the long tasks behind it.

In March 2024, Interaction to Next Paint replaced First Input Delay as the Core Web Vital for responsiveness. A lot of sites that comfortably passed FID do not pass INP, and the reason is not that they got slower. It is that the new metric looks at the part of the problem the old one ignored.

What FID measured, and what it missed

First Input Delay measured only the delay before the browser could begin processing the first interaction on the page. Not how long the handler ran. Not whether anything appeared afterwards. Not any interaction after the first.

That made FID very easy to pass. A page could accept your first tap instantly and then take a second to actually do anything about it, and FID would call that good. Most of what users experience as an unresponsive page was outside the metric.

What INP measures

INP measures the full latency of an interaction: from the user’s input until the browser paints the next frame showing the result. That is three phases.

  1. Input delay. From the input event until the event handler can start — the main thread was busy with something else.
  2. Processing time. How long your event handlers actually run.
  3. Presentation delay. From handlers finishing until the next frame is painted — style, layout, paint, composite, plus anything else that grabbed the thread in between.

INP watches clicks, taps and key presses across the whole page visit, not just the first one. Scrolling and hovering are not counted. The value reported is essentially the worst interaction of the visit, with a small allowance for outliers on pages with many interactions.

Google’s published thresholds: 200 ms or under is good, over 500 ms is poor, assessed at the 75th percentile of visits.

Why long tasks are the thing to hunt

A browser has one main thread per page, and it does everything: running your JavaScript, computing styles, laying out, painting. It does these one at a time. A long task is any main-thread task over 50 ms. While one runs, nothing else can happen — including reacting to the tap that just landed.

That is why long tasks show up in every phase of INP. A long task already running when the user taps becomes input delay. A long handler is processing time. A long task queued between the handler and the frame becomes presentation delay. Chasing long tasks is the single highest-leverage thing you can do for INP, and usually for LCP too, because a busy thread cannot paint either.

Common sources, roughly in order of how often they turn out to be the culprit:

Finding the culprit

In the field, use an INP measurement that reports attribution. The web-vitals JavaScript library’s attribution build reports the interaction target, the phase that dominated, and — via the Long Animation Frames API — which script kept the frame busy. This is the version worth having, because the alternative is knowing your INP is 480 ms and having no idea what the user tapped.

In the lab, record the interaction in the Chrome DevTools Performance panel, find the long task on the main thread flame chart, and read the call stack down to whatever is actually spending the time.

Do note that Lighthouse does not measure INP. It reports Total Blocking Time, which sums the time over 50 ms of every long task during load. TBT is a good load- time proxy — a page with high TBT will almost certainly have INP trouble — but a 0 ms TBT only tells you the page was quiet during load. Interactions after load, on components that were never exercised by a scripted run, are invisible to it. This is the most common way a site with excellent lab scores fails INP in the field.

What actually helps

Ship less JavaScript. Every other technique is a way of coping with having too much. This is the one that removes the problem.

Audit third parties against what they earn. Each one is a main-thread tenant. Load what survives the audit async/defer, or after interaction, or not at all.

Break up long tasks. Yield to the main thread between chunks of work so the browser gets a chance to handle input. scheduler.yield() is the modern way where it is supported; splitting work across setTimeout boundaries is the fallback.

Show the response before doing the work. INP ends at the next paint, not at the end of your logic. Update the UI — the pressed state, the spinner, the optimistic value — then do the expensive part in a later task. The user sees a response in one frame regardless of how long the work takes.

Do less work per interaction. Debounce input handlers. Avoid re-rendering trees that did not change. Move heavy computation off the main thread with a Web Worker where the work is genuinely CPU-bound.

Avoid forced synchronous layout. Batch DOM reads and DOM writes rather than interleaving them.

The uncomfortable summary

INP is harder to pass than FID because it measures what users actually complain about, on every interaction, for the whole visit. Sites built as a mostly-static document with a little JavaScript tend to pass it without effort. Sites that ship a framework, hydrate everything, and carry five third-party tags tend not to, and the fix is architectural rather than a setting to turn on.

Alfred Westerveld