Your Lighthouse report is mostly green. LCP is fine, CLS is fine, the page loads fast. Then the real-world score drops and you can't see why. Nine times out of ten the culprit is INP — and it's the one metric a quick Lighthouse run barely shows you.
What INP actually measures
INP, short for Interaction to Next Paint, replaced FID as a Core Web Vital in March 2024. FID only looked at the delay before your first interaction. INP looks at all of them, the whole time someone uses the page, and reports close to the worst one.
So it's not a loading metric. It's a responsiveness metric. It answers a different question: when I tap, click, or type, how long until the screen actually changes? Google's buckets are simple — 200ms or under is good, over 500ms is poor.
That's why a site can load in a second and still fail. Loading fast and responding fast are two different jobs, done by two different things.
Why it's invisible in a normal audit
LCP and CLS happen during load, so a lab tool catches them every run. INP only happens when a human interacts. Lighthouse doesn't tap your buttons, so its number is an estimate at best. You can have a green lab report and a red field score at the same time, and that gap is exactly where people get stuck.
To see the real number, measure interactions as they happen:
import { onINP } from 'web-vitals';
onINP(function (metric) {
console.log('INP', metric.value, metric.entries);
});
That logs the actual slow interaction and the element behind it. Now you're fixing a real thing instead of guessing.
What's really slow
INP is almost always one thing: the main thread was busy when the user acted. The browser can't paint the response until the current JavaScript task finishes, so a long task blocks the interaction.
The usual sources:
- A heavy event handler doing real work on every click or keystroke.
- Third-party scripts like chat widgets, analytics, and tag managers, running long tasks at the wrong moment.
- Layout thrash: reading and writing the DOM in a loop so the browser recalculates over and over.
- Framework hydration waking the whole page up at once.
The fixes that move it
Break up long tasks. If a handler does a lot, let the browser breathe partway through instead of holding the thread:
async function onClick() {
doUrgentPart(); // update the UI first
await yieldToMain(); // give the browser a turn to paint
doExpensivePart(); // the rest can wait a tick
}
yieldToMain is a one-line helper around scheduler.yield() where it's supported, or a setTimeout(0) fallback. The trick is to paint the response before the slow work, not after.
Beyond that: defer scripts the page doesn't need to react, audit third-party widgets for the ones that run long tasks, debounce expensive handlers, and batch your DOM reads and writes so the browser isn't recalculating layout on every line.
The honest part
I won't promise you a magic number — INP depends on your scripts, your theme, and what your users actually click. But it's measurable, and the field data shows the difference plainly once the long tasks are gone.
I keep my own WordPress sites in the green on Core Web Vitals, and INP is the one I watch most now, because it's the one that quietly fails while everything else looks fine. If your lab report is green but the real score isn't, stop staring at LCP. Go measure an interaction.
Top comments (0)