A red INP score is not a performance problem until you know which of three phases is eating the time. Most teams skip that step, buy a caching plugin or defer a script, and watch the 75th percentile refuse to move. This guide is the decision tree we run instead: seven branches that take you from a failing Chrome UX Report number to the specific handler, script or third-party tag responsible, with a stated threshold at every fork. It is written for front-end leads and growth teams who own a conversion number as well as a Lighthouse score.
The gap is real. In the 2025 Web Almanac, 77% of mobile origins had a good INP against 97% on desktop, and only 48% of mobile origins passed all three Core Web Vitals (HTTP Archive). What is still broken on mobile is mostly interaction latency, and it is mostly diagnosable.
What INP measures, and what it quietly ignores
Interaction to Next Paint reports one interaction latency per page visit, at the 75th percentile across your origin. Good is 200 ms or less, needs improvement is above 200 ms and up to 500 ms, poor is above 500 ms (web.dev). Three things about that definition change how you debug it:
- Only clicks, taps and key presses count. Hovering, zooming and scrolling are not measured, so a janky scroll can make a page feel broken while INP stays green.
- On pages with many interactions, INP ignores one highest interaction for every 50, so a rare catastrophic interaction can hide on a high-engagement page and surface on a low-engagement one.
- Every interaction splits into input delay (before your handlers run), processing duration (your handlers running) and presentation delay (after your handlers, before the frame paints). These three have almost nothing in common as engineering problems (web.dev).
That last point is the whole method. You are not debugging "INP". You are debugging one of three phases, and each has a different fix.
Branch 0: confirm the number is real before you touch code
Open PageSpeed Insights and read the field section, not the lab score. Lab tools cannot measure INP, because INP needs a real user pressing something. If the origin has no field data, you have a traffic problem, not a performance problem, and nothing below applies yet. Then pull mobile and desktop separately — globally they differed by 20 percentage points in 2025. If mobile is red and desktop is green, stop testing on your laptop.
Branch 1: is the slow interaction happening during load or after it?
Instrument the field with the web-vitals attribution build. Four lines get you the data:
import {onINP} from 'web-vitals/attribution';
onINP(({value, rating, attribution}) => {
navigator.sendBeacon('/rum', JSON.stringify({value, rating, attribution}));
});
Read attribution.loadState first. If most bad interactions carry a load state of loading or dom-interactive, users are tapping while your scripts are still evaluating: a delivery problem, fixed at the bundle and the tag manager, not in the handler. If bad interactions are spread across fully loaded pages, go to Branch 2.
Branch 2: which subpart owns the time?
Log inputDelay, processingDuration and presentationDelay for every sample. Our working rule: a subpart owns the interaction when it accounts for more than 50% of total INP. If none clears 50%, treat it as an input-delay case first — diffuse main-thread congestion is usually what makes the other two worse.
| Dominant subpart | Investigate first | Typical cause | First fix |
|---|---|---|---|
| Input delay > 50% | longAnimationFrameEntries invoker type | Script evaluation during load; timers; a previous interaction still running | Defer or remove third-party scripts; split bundles; move periodic work off-thread |
| Processing duration > 50% | longestScript and totalScriptDuration | One expensive event handler; a state update that triggers a full re-render | Yield inside the handler; move non-visual work after the next paint |
| Presentation delay > 50% | totalStyleAndLayoutDuration | Large DOM; layout thrashing; heavy requestAnimationFrame work | Reduce DOM size; apply content-visibility; batch style reads and writes |
| No subpart > 50% | All of the above | General main-thread congestion | Treat as input delay, re-measure, then re-branch |
Branch 3: name the script
The attribution build carries Long Animation Frame entries alongside each INP sample. attribution.longestScript gives you the invoker, the function and the source file for the worst script in the interaction; totalScriptDuration aggregates everything over 5 ms. This is where a vague "React is slow" becomes a file path. Check the invoker type: classic-script or module-script points at load-time evaluation, while user-callback or event-listener points at something running after load — an observer, a timer, an analytics listener (web.dev).
Branch 4: is it yours or a third party's?
Sort your script attribution by origin. Tag managers, consent platforms, session-recording tools and chat widgets install global listeners that run before yours on every click. If a third-party origin appears in more than a quarter of your worst samples, the fix is commercial before it is technical: reduce the tag's scope, load it on interaction, or move it server-side. Do not refactor your handler to buy back 40 ms a chat widget spends anyway.
Branch 5: reproduce it in the lab, then fix the frame
Take the selector from attribution.interactionTarget, open Chrome DevTools with CPU throttling matched to your field device mix, and record the interaction. Look for a task over 50 ms, the long-task threshold (web.dev), and split it by yielding.
Ship scheduler.yield() with a fallback. Chrome and Edge support it from 129 and Firefox from 142, but Safari does not, on desktop or iOS, and MDN marks it limited availability rather than Baseline (MDN, caniuse). For a Turkish or Gulf audience with a large iOS share, a setTimeout fallback is not optional.
Branch 6: measure the market you actually sell to
Global CrUX averages hide the market that pays you. The CrUX BigQuery dataset publishes country tables — chrome-ux-report.country_tr.YYYYMM for Turkey, the same pattern for any two-letter country code — refreshed monthly, with device form factor and effective connection type as dimensions (Chrome for Developers). The public CrUX API does not expose country, so this slice is BigQuery-only.
Run your origin against the country table for every market you sell into. A retailer serving Türkiye, Germany and the Gulf routinely finds three different INP distributions off one codebase, because device mix and network conditions differ. Fix the market with the revenue, not the one with your office in it.
Branch 7: prove it moved something
Treat the fix as an experiment, not a deploy. Trendyol, a Turkish marketplace, traced a 963 ms mobile INP to one 700–900 ms long task caused by an IntersectionObserver callback inside a React component, deferred the state update with scheduler.yield(), and cut 75th-percentile INP from roughly 1,400 ms to roughly 650 ms over six months. Its A/B test showed a 1% uplift in click-through from listings to product detail per session (web.dev). redBus took lazy-load interactions from 870–900 ms to 350–370 ms, improved a text-input interaction by 72%, and reported a 7% increase in overall sales (web.dev).
Note the honest scale: 1%, not 40%. Which brings us to a number worth correcting.
The 8.4% figure deserves a caveat
The most quoted number in this field is that a 0.1 s improvement in mobile speed lifts retail conversion by 8.4% and travel by 10.1%. It comes from a real study — 55 and Deloitte, commissioned by Google, 37 brands, over 30 million sessions (web.dev). But it measured load metrics such as page load time and server latency, not INP; the data covers 30 days at the end of 2019; and there was no control group, so it is an observed correlation, not a causal effect. Use it to open a budget conversation, not to forecast the return on an interaction-latency fix.
[INTERNAL DATA NEEDED: Switas before/after p75 INP and conversion delta from a client engagement, to replace the public case studies with first-party evidence.]
Where this decision tree breaks down
- Low-traffic origins. With thin field data the 75th percentile is noise. Below roughly 1,000 interactions per template per week, profile in the lab and accept that you cannot verify the win in CrUX.
- Safari and Firefox users are invisible. CrUX is Chrome-only. If a meaningful share of your revenue arrives on iOS Safari, your INP data describes a population you are not fully measuring.
- Scroll jank is out of scope. INP ignores scrolling, so this tree will never find the problem in an infinite-scroll listing that feels terrible but tests green.
- It finds latency, not design debt. A 180 ms interaction on a confusing filter panel is a UX problem this tree cannot see.
FAQ
Is INP the same thing as First Input Delay?
No. FID measured only the delay before the first interaction's handlers ran. INP covers the full latency of a representative interaction across the visit, including handler execution and the paint that follows.
Why does my Lighthouse score look fine while INP is red?
Lighthouse is a lab tool and cannot measure INP: there is no real user pressing anything. Proxies such as Total Blocking Time correlate loosely. Read the field section of PageSpeed Insights instead.
How long before a fix shows up in CrUX?
CrUX reports on a 28-day rolling window, and the BigQuery tables publish monthly. Expect a partial signal after about four weeks and a clean read after two monthly releases.
Can I fix INP without touching the framework?
Often yes. Trendyol's fix was a yield inside one callback on React 16.9, with no upgrade. Deferring third-party tags is framework-independent too.
Does a consent banner affect INP?
It can, in two ways: the banner's own script competes for the main thread during load, and the consent decision often triggers a burst of tag loading immediately afterwards. Measure the first interaction after consent separately.
Do I need a RUM vendor, or is the web-vitals library enough?
The attribution build plus a beacon endpoint is enough to run this tree. A vendor buys retention, segmentation and dashboards, not better attribution data.
Should I optimise INP for SEO or for conversion?
Decide for conversion. Core Web Vitals are a ranking signal, but the documented business cases — 1% at Trendyol, 7% at redBus — are interaction-quality effects that justify the work on their own.
Run this on your own listing page
Take your highest-traffic template, instrument it with the attribution build, and walk Branches 0 through 7 in order. If you would rather have the tree run against your own field data, with the country slice and the experiment design included, our web performance team does exactly this engagement.
Sources
- Interaction to Next Paint (INP) — web.dev
- Optimize Interaction to Next Paint — web.dev
- Find slow interactions in the field — web.dev
- Optimize long tasks — web.dev
- Performance — Web Almanac 2025, HTTP Archive
- How Trendyol reduced INP by 50% — web.dev
- How redBus improved INP and increased sales by 7% — web.dev
- Milliseconds make millions — web.dev / 55 / Deloitte
- CrUX on BigQuery — Chrome for Developers
- Scheduler: yield() — MDN
- Scheduler API: yield — caniuse
- web-vitals JavaScript library — GoogleChrome/web-vitals







