Why is my INP failing when Lighthouse says the page is fast?
Because Lighthouse cannot measure INP. It is a field metric that requires a real person clicking something, and the lab score you are reading is an estimate of a different number.
By Sapun Lamichhane · Arcetis
Because Lighthouse never clicked anything. INP measures how long the browser takes to paint a response after a real interaction, and a lab run that loads a page and walks away has no interaction to measure.
What Lighthouse gives you instead is Total Blocking Time: how long the main thread was blocked during load. It correlates with INP well enough to be useful and it is not the same number, which is why a 98 in Lighthouse and a failing INP in Search Console are not a contradiction. They are two different measurements of two different moments.
Lab data and field data
Every performance number you see comes from one of two places, and confusing them wastes an enormous amount of time.
- **Lab data** is a synthetic run: one load, one simulated device, one network profile, no user. Lighthouse and PageSpeed Insights' top section are lab. It is reproducible and diagnostic.
- **Field data** is what actually happened to real Chrome users over the last 28 days, aggregated in the Chrome UX Report. Search Console's Core Web Vitals report and the top of PageSpeed Insights are field. It is the data Google uses.
INP only exists in field data. If your page has too little traffic to appear in CrUX, you have no INP at all - which is not the same as passing.
Why the 75th percentile matters more than you think
The threshold is not your average interaction. It is the 75th percentile: one interaction in four must be worse than your reported number for you to fail. A page can feel instant to you on a desktop and fail INP on the strength of its slowest quarter - mid-range Android phones, a cold cache, a heavy third-party script that loads on some sessions and not others.
It also measures the worst interaction on the page, not the first. FID only ever looked at the first input. INP watches all of them, which means a page that loads fast and then bogs down once a data grid is populated now fails where it used to pass.
What actually causes bad INP
- 1**Long tasks on the main thread.** Any task over 50ms blocks the browser from responding. React re-rendering a large list synchronously on click is the textbook case.
- 2**Event handlers doing real work.** A click handler that filters ten thousand rows before yielding delays the paint by exactly as long as it takes.
- 3**Layout thrashing.** Reading a layout property, writing a style, then reading again forces synchronous reflow on every cycle.
- 4**Third-party scripts.** Analytics, tag managers, chat widgets and consent banners all run on your main thread, and consent banners in particular run at exactly the moment users first interact.
- 5**Hydration.** A large client component tree that hydrates on interaction can block the very interaction that triggered it.
How to measure it properly
Stop reading the lab score and get the real one:
- Search Console → Core Web Vitals shows your field INP, grouped by URL pattern. This is what Google sees.
- PageSpeed Insights shows CrUX field data at the top - above the Lighthouse section. Read the top half.
- Chrome DevTools → Performance panel, recorded while you interact, shows which task blocked the paint and for how long.
- The `web-vitals` library reports INP from your own users, including the specific element that caused the worst interaction, which is the single most useful diagnostic available.
Fixing it
- Break long tasks up. Yield to the main thread with `scheduler.yield()` where available, or `setTimeout(0)`, so the browser can paint between chunks.
- Paint first, compute second. Update the visual state immediately, then do the expensive work in the next frame. Users read responsiveness from the paint, not the result.
- Defer third-party scripts that do not need to run during load, and audit consent banners specifically.
- Virtualise long lists rather than rendering every row.
- Reduce how much of the page is a client component. Work that never ships to the browser cannot block it.
Our performance checker reports lab metrics and labels them as lab, rather than presenting a synthetic score as though it were what your users experienced. Where field data exists for the URL, it is shown alongside - because the two disagreeing is information, not an error.