Making sense of INP, the Core Web Vital nobody asked for
In March 2024, Google swapped out First Input Delay for Interaction to Next Paint as the responsiveness metric in Core Web Vitals. A lot of sites that had comfortably passed FID for years suddenly found themselves failing, and plenty of teams spent that year explaining to stakeholders why a metric nobody had heard of was now flagging their site in Search Console.
More than two years on, INP is still the Core Web Vital that trips people up most, partly because it is genuinely harder to fix than the others, and partly because most explanations of it are either too shallow or too academic. This guide aims for the middle.
What INP measures, and why FID had to go
FID measured one thing: the delay between a user's first interaction and the moment the browser could start processing it. First interaction only, delay only. It ignored how long the processing itself took, and it ignored every interaction after the first one. A page could freeze for two seconds every time you clicked a filter, and as long as the very first tap got picked up quickly, FID gave it a passing grade. Almost everyone passed. A metric everyone passes is not measuring anything useful.
INP fixes both blind spots. It observes every click, tap, and keypress during the entire page visit, measures the full time from interaction to the next visual update, and reports roughly the worst one (technically a high percentile, which for most pages means the slowest interaction). Your score is your worst moment, not your best first impression.
The thresholds: 200 milliseconds or under is good, 200 to 500 needs improvement, over 500 is poor, all measured at the 75th percentile of real users. Note what INP does not cover: hover effects and scrolling are out of scope.
The three phases of every interaction
Every INP value is the sum of three phases, and knowing which phase is eating your budget tells you what to fix.
Input delay: the time between the user's interaction and your event handler starting to run. If the main thread is busy with something else (a long task, a third-party script, an analytics beacon), the interaction waits in line. This is roughly what FID used to measure.
Processing duration: how long your event handlers actually take. Heavy state updates, synchronous layout reads, big loops over data, all of it lands here.
Presentation delay: the time from handlers finishing to the browser painting the next frame. Big DOM updates, expensive style recalculation, and layout thrashing live in this phase.
The distribution is rarely even. On content sites, input delay tends to dominate because third-party scripts hog the main thread. On web apps, processing duration tends to dominate because click handlers do too much work synchronously. Knowing your site's profile before you start optimizing saves weeks.
Field data vs lab data (and why Lighthouse cannot save you)
Here is the thing that confuses everyone: INP is a field metric. It requires a real user actually interacting with your page. Lighthouse loads a page and never clicks anything, so a standard Lighthouse run cannot measure INP at all. It offers Total Blocking Time as a lab proxy, and TBT correlates with INP, but correlation is not identity. Teams routinely grind TBT down in lab runs while their field INP barely moves, because the interaction that hurts real users happens three minutes into a session on a filter button Lighthouse never touched.
Your source of truth is CrUX, the Chrome User Experience Report, which is where Search Console and PageSpeed Insights get their field numbers. That data is a 28-day rolling window, so any fix you ship takes up to a month to fully show up. Set that expectation with stakeholders early, or the week-two question of why the number has not moved will find you.
Lab tools still matter, but for diagnosis, not measurement. You reproduce the slow interaction locally, fix it, then wait for the field to confirm.
The usual suspects
Across audits since the switch, the same culprits keep showing up:
Long tasks. Any main-thread task over 50 milliseconds blocks interactions from being processed. One 800-millisecond task in the wrong place ruins every interaction that lands during it.
Third-party scripts. Tag managers, consent platforms, chat widgets, session recorders. They all compete for the same main thread your event handlers need, and consent tooling in particular loves to run right when users start tapping.
Hydration. On JavaScript frameworks, the page looks interactive before it is. A user taps a button mid-hydration and the interaction queues behind the entire hydration task. Frameworks have gotten better here, but big client-side bundles still produce exactly this failure.
Doing everything in the click handler. Updating state, recalculating a giant list, firing three analytics events, and rendering, all synchronously, before the browser is allowed to paint.
Excessive DOM size. Tens of thousands of nodes make every style and layout pass slower, which inflates presentation delay on every single interaction.
Diagnosing it with DevTools and CrUX
A working sequence for an INP investigation:
Start with PageSpeed Insights or the CrUX dashboard to confirm the field number and see whether the problem is site-wide or concentrated on specific templates.
Check Search Console's Core Web Vitals report to find which URL groups are failing. Templates fail together, so one diagnosis usually covers thousands of URLs.
Open Chrome DevTools, go to the Performance panel, and start a recording while you actually use the page: click filters, open menus, type in search. Interactions get flagged directly in the timeline with their INP breakdown.
Look at the phase breakdown for the slow interaction. Long input delay means find the long task that was running. Long processing means profile your handler. Long presentation delay means look at rendering cost.
Use the web-vitals JavaScript library in the field with attribution enabled if you need to know which real elements users are struggling with. It reports the exact element and phase timings to your analytics.
Fixes that actually work
The honest list, roughly in order of effort versus payoff:
Yield to the main thread. Break long tasks apart so interactions can jump the queue. The scheduler.yield API exists for exactly this in Chromium browsers, with setTimeout as the crude fallback.
Give feedback first, compute later. In the handler, update the UI immediately (spinner, pressed state), then defer the heavy work to after the next paint. INP measures time to next paint, not time to finished work.
Audit your tag manager. Most containers carry at least a couple of tags nobody can justify. Every one you remove gives main-thread time back.
Ship less JavaScript. Less code to parse and execute means shorter tasks, faster hydration, quieter main thread. Server rendering with minimal client JS remains the most reliable INP strategy there is.
Debounce input handlers and virtualize long lists so keystrokes and clicks touch less DOM.
Avoid layout thrashing: do not read layout properties right after writing styles inside a handler.
What does not work: hiding the problem behind a loading overlay, or optimizing your homepage when the failing interactions live in checkout.
Nobody asked for it, but it was the right call
INP is annoying because it measures something real. FID let sites feel broken and score perfectly. INP catches the frozen filter, the laggy menu, the dead-feeling add-to-cart button, the stuff users actually rage-tap. It is harder to pass because the bar actually means something now. Fix the worst interaction on your highest-traffic template, wait out the 28-day window, then move to the next one. Boring, incremental, effective.