Interaction to Next Paint (INP) replaced First Input Delay as a Core Web Vital in March 2024, and it's still the one most sites fail. It measures what users feel: how long after a tap, click or keypress the page visibly responds, across the whole visit rather than only the first input.
How INP works
For each interaction, INP measures three phases:
- Input delay: time waiting for the main thread to be free
- Processing time: running your event handlers
- Presentation delay: style, layout and paint of the next frame
The page's INP is roughly its worst interaction, ignoring outliers. Google assesses it at the 75th percentile of real users:
| INP | Rating |
|---|---|
| ≤ 200 ms | Good |
| 200–500 ms | Needs improvement |
| > 500 ms | Poor |
web.dev's INP guide is the canonical reference.
Step 1: find the slow interactions in the field
Lab tools can't click like your users. Use field data:
- Search Console's Core Web Vitals report, to see which URL groups fail
- The web-vitals library with the attribution build, sending INP, the target element and the phase breakdown to your analytics
- The Long Animation Frames (LoAF) API, to attribute slow frames to specific scripts
You want a list like: "Add to cart button on product pages, 480 ms, mostly processing time, script: reviews-widget.js".
Step 2: reproduce in the lab
Open Chrome DevTools → Performance, enable 4× CPU throttling, and perform the interaction. Look for long tasks (over 50 ms) after the click and see which functions dominate.
Step 3: apply the right fix
High input delay: the main thread was busy
- Defer third-party scripts (chat, reviews, A/B testing, heatmaps) until after load or first interaction
- Audit the tag manager. It's the single biggest INP offender we see on marketing and e-commerce sites
- Break up long hydration work. In Next.js, keep components as Server Components unless they need interactivity
Long processing time: your handler does too much
- Do the visible update first, then defer the rest:
button.addEventListener("click", async () => {
showSpinner(); // visible feedback, immediately
await scheduler.yield(); // let the browser paint
await updateCartAndAnalytics();
});
- In React, wrap non-urgent updates in
startTransitionoruseTransition, so typing and clicking stay responsive - Debounce search and filter inputs; don't re-filter 2,000 products on every keystroke
- Move heavy computation to a Web Worker
Long presentation delay: rendering is expensive
- Reduce DOM size. Long product grids and mega-menus are common culprits
- Use
content-visibility: autofor off-screen sections - Avoid layout thrashing: don't read layout (
offsetHeight) and then write styles in a loop - Animate
transformandopacityonly
Platform-specific tips
Next.js and React
- Keep client components small and leaf-level
- Memoise expensive lists, and virtualise long ones
- Stream data with Suspense instead of blocking interactions; see our Cache Components guide
Shopify themes
- Remove apps you no longer use. Their scripts often stay behind
- Load app blocks only on templates that need them
- Prefer native theme features over app widgets for filters and quick-add
- On very app-heavy stores, consider headless for full control
Step 4: guard against regressions
- Track INP from the field on a dashboard, segmented by template
- Set a performance budget for JavaScript per route in CI
- Review every new third-party script like a dependency, because it is one
Why it matters
INP is part of Google's page-experience signals, but the bigger win is commercial: responsive filters, add-to-cart buttons and checkouts convert better. This is especially true under peak load, so make INP part of your Black Friday readiness.
Bottom line
Measure in the field, find the worst interactions, then yield, defer and simplify. Most sites can get from "poor" to "good" in one or two focused sprints.
Our web development and e-commerce teams run performance sprints with field-data dashboards before and after. Get a performance review.
Frequently asked questions
What is a good INP score?
200 milliseconds or less at the 75th percentile of real page loads is good, 200–500 ms needs improvement, and over 500 ms is poor.
Why does my site fail INP when Lighthouse looks fine?
Lighthouse runs a lab test without real interactions, so it can't measure INP directly. INP comes from field data (real users clicking and typing), which is what Google's Chrome UX Report and Search Console use.
What usually causes poor INP?
Long JavaScript tasks on the main thread: heavy event handlers, large React re-renders, third-party scripts and tag managers, and big DOM updates that force expensive layout and paint.