The INP Fix Guide: From 600ms to Under 200ms
INP still fails on most sites in 2026. Here is why interactions hit 600ms — long tasks, main-thread bloat, layout thrash — and the five fixes under 200ms.
On this page
- Key takeaways
- What does INP measure?
- Why is INP the hardest Core Web Vital?
- Why is my INP 600ms?
- 1. React/Vue reconciliation
- 2. Synchronous heavy work
- 3. Layout thrash
- 4. Long tasks
- How do you fix INP?
- 1. Code-split everything
- 2. Yield to the main thread
- 3. Virtualize lists
- 4. Defer non-critical work
- 5. Pre-compute
- How do you find the slow interaction?
- How do you measure INP the right way?
- How do you keep INP from regressing?
- What not to do for INP
- Frequently asked questions
- What is a good INP score?
- Why is INP worse on mobile than desktop?
- Does lazy loading help INP?
- How long until an INP fix shows in Search Console?
- Is INP a ranking factor?
- The verdict
INP replaced FID in 2024. Two years later, most sites still score poorly. Here is the fix, stated first: a 600ms INP is almost never a network problem or a rendering problem — it is the main thread being busy when the user interacted. Cut the long tasks, yield the thread, and defer everything the user did not ask for, and you get under 200ms.
INP is the Core Web Vital that punishes modern JavaScript the hardest, because it measures what your code actually does at runtime, not what your bundle size promises. A React site that passes LCP and CLS can still fail INP on every filter click and every add-to-cart tap.
This guide walks the whole path: what INP measures, the four causes of a 600ms score, the five fix patterns, the diagnostic workflow, and the measurement loop that proves the fix worked.
Key takeaways
- INP is your slowest interaction, not your average one — tap, click, or key press, scored end-to-end across input delay, processing time, and presentation delay. Good is under 200ms.
- A 600ms score almost always means one long task, and anything over 50ms blocks the main thread from answering the user.
- The four usual culprits: React/Vue reconciliation, synchronous heavy work like parsing a 500KB JSON response, layout thrash inside event handlers, and unchunked long tasks.
- The five fix patterns: code-split, yield to the main thread, virtualize long lists, defer non-critical work, and pre-compute what you can move out of the click handler.
- Lab tools are regression alarms, field data is the verdict — measure INP with real users, never with
performance.now()in a test suite. - Disabling JavaScript to game the score is malpractice — INP is a proxy for user experience, and the moment you treat it as the target, you have failed the user.
What does INP measure?
INP measures the slowest interaction in a session — tap, click, key press — end-to-end and across three phases: input delay, processing time, and presentation delay. The target is under 200ms; between 200ms and 500ms is needs improvement; anything above 500ms is poor.
The three phases each fail for different reasons. Input delay is the time between the user’s action and your handler starting — it grows when something else is on the thread. Processing time is your event handler running — state updates, loops, parses. Presentation delay is the frame after your work: styles, layout, paint until the next frame is visible. A fix that targets the wrong phase is wasted effort, which is why diagnosis comes before prescriptions in this guide.
Why is INP the hardest Core Web Vital?
INP is the hardest Core Web Vital because it is runtime-dependent, user-dependent, and session-long — a page can be light, fast-loading, and still fail every interaction once the app state grows. LCP and CLS are mostly page-architecture problems you fix once; INP is a behavior problem that resurfaces with every feature.
It is also the metric most exposed to the modern frontend. Heavy frameworks re-render on every state change, analytics tags run inside click handlers, and feature work ships one more synchronous loop onto a thread that was already crowded. The JavaScript that made your app possible is the JavaScript that makes it feel slow — which is exactly the point of our broader treatment of JavaScript SEO, where the same rendering cost is argued from the crawler’s side. Images, meanwhile, are the LCP and CLS siblings — the image SEO playbook covers that half of the vitals.
Why is my INP 600ms?
Because the main thread is busy when the user taps. Input arrives, and the browser cannot start your handler until whatever long task is running finishes; by the time your code has also run and the next frame paints, hundreds of milliseconds have passed. Four patterns cause most of it.
1. React/Vue reconciliation
Large component trees re-render on every state change. A filter input that re-renders a 3,000-row table plus sidebar plus header spends most of its keystrokes inside reconciliation — hundreds of milliseconds of virtual-DOM diffing that the user experiences as a dead keyboard.
2. Synchronous heavy work
Anything you do in the handler blocks the handler. The classic: a JSON parse of a 500KB response on the main thread, or a sort over a large array, triggered by the very click that should show results instantly.
3. Layout thrash
Forced reflow inside event handlers. You read a layout property, write a style, read again — and the browser recalculates geometry each time. Ten iterations of that pattern inside a scroll or click handler is a 300ms bill.
4. Long tasks
A long task is anything over 50ms blocking the main thread. The browser cannot preempt it; every interaction during that window queues behind it. A handful of these per page view — analytics bootstraps, third-party tags, giant hydration passes — is the most common single cause of poor INP.
INP is not a rendering metric. It is a responsiveness tax, and the main thread always pays it.
How do you fix INP?
You fix INP by removing work from the main thread at interaction time: split the code, yield the thread, virtualize what you render, defer what the user didn’t request, and pre-compute what you can move earlier. All five patterns below are ordered by how often they rescue a real site — most 600ms cases fall to the first three combined.
1. Code-split everything
Route-level splits are table stakes — every screen should load only its own code. Beyond that, split per-component for heavy islands: editors, charts, maps, and modals should not ship to users who never open them. Less code on the page means less hydration and less runtime work inside your handlers.
2. Yield to the main thread
Long work should not run uninterrupted. Use scheduler.yield() or break long work into microtasks so the browser gets gaps between chunks to handle pending input. The discipline: no task above ~50ms; chunk loops so the thread stays responsive between pieces.
3. Virtualize lists
A 10,000-row table rendered at once is an INP failure waiting to happen. Render only the rows inside the viewport plus a small overscan, and swap the DOM as the user scrolls. The browser never pays to layout thousands of hidden rows.
4. Defer non-critical work
Logging, analytics payloads, share-state syncs, and feature flags don’t belong in the click path. Use requestIdleCallback, scheduler.postTask, or a worker to run them when the thread is free — or in another thread entirely.
5. Pre-compute
Don’t filter a 10,000-item array in a click handler. Do it once when the data arrives, cache the result by key, or move the computation to a worker. Interaction time is for responding, not for arithmetic the user can’t feel the difference of receiving two frames later.
How do you find the slow interaction?
You find a slow interaction by capturing real INP, reproducing it in a profiler, and reading the flame chart down to the task that blocks. The three tools that cover this loop are complementary, not interchangeable — each sees a different slice of the problem.
- Web Vitals Chrome extension — captures real-user INP, so you know which interactions fail in the wild, not which ones might.
- Chrome DevTools Performance panel — the flame chart of the slow interaction itself; record, click, and look for tasks over 50ms starting from your event handler.
- Lighthouse — synthetic but useful for regressions: it won’t measure true INP, but it flags the long-task patterns that cause it and guards your CI against backsliding.
The workflow we run: reproduce on a CPU-throttled device, record while performing the failing gesture, and inspect the call stack between input and next paint. The largest block you find is the task to fix — then apply the pattern from the previous section that matches its shape, not all five.
How do you measure INP the right way?
Measure INP the way Google scores it: field data, 75th percentile, mobile first. The Search Console Core Web Vitals report and CrUX give you the real-user p75; the Web Vitals extension gives it per page while you work. If p75 is under 200ms, you pass — regardless of what Lighthouse says.
Two measurement rules save you from self-deception. First, lab numbers are hypotheses, field numbers are evidence; a single 40ms interaction in a synthetic test means nothing if real devices show 300ms. Second, don’t measure with performance.now() in tests — INP is user-facing, and it includes input and presentation delay you cannot see from inside your own function. Finally, CrUX aggregates over 28 days, so fixes show up in Search Console over weeks, not hours; use the field-extension numbers for immediate feedback while you wait for the official verdict. If a Core Update has you chasing rankings back, responsiveness sits on that recovery list — see our core update recovery playbook.
How do you keep INP from regressing?
You keep INP from regressing by putting responsiveness on a budget, the same way you budget bundle size — because one careless commit can undo a quarter of fixes, and INP decays silently between audits. Treat it as a guardrail problem, not a one-time cleanup.
The practical stack: a Lighthouse CI job on every pull request as the synthetic regression alarm; real-user monitoring wired to the web-vitals library so you see field numbers daily instead of waiting on CrUX’s 28-day window; and a written rule that no task over 50ms ships on an interaction path. Third parties need the same discipline — an analytics or A/B tag that runs inside click handlers will bloat your scores without touching your code. Framework upgrades matter too: a hydration change or a new shared-context provider can quietly re-add reconciliation cost to every keystroke, and only the budget catches it.
What not to do for INP
The don’ts are rules against gaming the metric instead of fixing the experience. INP is a proxy for how responsive the page feels; the moment you optimize the number while degrading the product, you have inverted the point and usually traded ranking for revenue or trust.
- Don’t disable JavaScript to game the score. No code, no long tasks, no interactions worth measuring — and no product. You can pass INP on a broken page; you cannot run a business there.
- Don’t ship a service worker that throttles itself. Self-imposed delays that flatter a single run are fragile, invisible to your team, and a fresh regression on the next release.
- Don’t measure with
performance.now()in tests — it cannot see input delay or presentation delay, the two phases you are actually being scored on.
The honest version of INP work is less clever and more durable: fewer and smaller tasks at the moment users touch something. That is also what the Core Web Vitals guide treats as the whole point of the vitals.
Frequently asked questions
What is a good INP score?
Under 200ms at the 75th percentile of real users, mobile and desktop. Between 200ms and 500ms is needs improvement; above 500ms is poor. Because INP records the worst interaction of a session, one unchunked 600ms task can sink the whole page’s score.
Why is INP worse on mobile than desktop?
Mobile CPUs are slower and more often thermal-throttled, which stretches every millisecond of processing time, and mobile input adds its own event-loop overhead. The same handler that runs in 150ms on a laptop can easily exceed 450ms on a mid-range phone.
Does lazy loading help INP?
Script lazy loading and code-splitting help — less JavaScript on the main thread at interaction time means less reconciliation and less work inside handlers. Lazy-loading images is an LCP and CLS lever, not an INP one.
How long until an INP fix shows in Search Console?
Field data aggregates over 28 days, so the Core Web Vitals report lags your deploy by weeks. Track the per-page numbers with the Web Vitals extension for instant feedback and treat Search Console as the rolling verdict.
Is INP a ranking factor?
Yes — it is one of the Core Web Vitals, which feed Google’s page-experience assessment. The practical framing: fixes that move INP under 200ms also remove the lag users complain about, so ranking and retention improve from the same work.
The verdict
A 600ms INP is not a mystery and not a network problem — it is scheduled work colliding with an unscheduled user. Identify the long task, then apply the matching pattern: split it, yield it, virtualize it, defer it, or pre-compute it. One task removed from the click path is worth a hundred micro-optimizations elsewhere.
Keep the loop honest: fix with the flame chart, verify with field data at the 75th percentile, and never trade the measurement for the experience. The deeper ranking context lives in the core web vitals guide, and the script-level causes are covered in our JavaScript SEO guide breakdown.
Run the 45-Point SEO Analyzer
Run 45-Point SEO Analyzer on your site — instant score. No signup, no server calls.
Stay updated on SEO changes
We send out a quarterly digest of search volatility and algorithm updates. No spam, ever.
Frequently asked questions
The questions readers ask before they put the playbook to work.
