Fixing INP: A Developer's Guide to Faster Interactions and Better Rankings
Interaction to Next Paint (INP) measures how quickly your site responds to taps, clicks and key presses. Here's how to find slow interactions and fix them in React and Next.js apps.

Ahsan Bilal
Principal Software Engineer
On this page
Your page loads fast, scores well in Lighthouse — and still feels sluggish when people tap a button or open a menu. That gap is what Interaction to Next Paint (INP) measures. It replaced First Input Delay as a Core Web Vital in 2024, and it's the metric many otherwise-fast React sites struggle with.
What INP measures
INP looks at the interactions a user makes during a visit — clicks, taps and key presses — and reports one of the slowest: the time from the interaction until the browser paints the next frame. Google's thresholds, measured at the 75th percentile of real visits:
| INP | Rating |
|---|---|
| 200 ms or less | Good |
| 200–500 ms | Needs improvement |
| Over 500 ms | Poor |
Every interaction has three phases, and any of them can be the problem:
- Input delay — the main thread is busy with something else when the user taps.
- Processing time — your event handlers (and the React re-render they trigger) run.
- Presentation delay — the browser recalculates styles, lays out and paints.
Step 1: Measure real users, not just lab tests
Lighthouse can't fully measure INP because it doesn't interact like a user. Start with field data:
- Search Console → Core Web Vitals shows which URL groups fail on mobile and desktop.
- PageSpeed Insights shows field data for a URL when enough visits exist.
- Your own RUM: the
web-vitalslibrary reports INP with attribution — which element, which phase, which script.
import { onINP } from "web-vitals/attribution";
onINP(({ value, attribution }) => {
sendToAnalytics({
metric: "INP",
value,
target: attribution.interactionTarget,
inputDelay: attribution.inputDelay,
processing: attribution.processingDuration,
presentation: attribution.presentationDelay,
});
});In Next.js, useReportWebVitals from next/web-vitals gives you the same data. Then reproduce the slow interaction locally in the Chrome DevTools Performance panel with CPU throttling on — mid-range phones are much slower than your laptop.
Step 2: Reduce input delay
Input delay means something else was hogging the main thread. Common culprits:
- Hydration of large component trees right after load. Keep client components small; render static content as Server Components so it never hydrates.
- Third-party scripts — chat widgets, tag managers, heatmaps. Load them after the page is interactive, or move them off the main thread.
- Long tasks from timers, analytics batching or large JSON parsing.
Break long tasks into smaller ones so the browser can respond in between. scheduler.yield() is available in Chromium-based browsers; fall back to a timeout elsewhere:
const yieldToMain = () =>
"scheduler" in globalThis && "yield" in (globalThis as any).scheduler
? (globalThis as any).scheduler.yield()
: new Promise((resolve) => setTimeout(resolve, 0));
for (const batch of chunks(items, 50)) {
process(batch);
await yieldToMain();
}Step 3: Make handlers and re-renders cheaper
Processing time is usually your re-render, not the handler itself.
- Update the urgent thing first. Show the pressed state, open the menu, echo the keystroke — then defer expensive work with
startTransitionoruseDeferredValueso React can paint in between. - Narrow state. State held high in the tree re-renders everything below it. Move it closer to where it's used, or split contexts.
- Virtualize long lists instead of rendering thousands of rows.
- Avoid layout thrashing — reading layout (
getBoundingClientRect) after writing styles in the same handler forces synchronous layout. - The React Compiler can remove much manual memoization work; profile before and after rather than sprinkling
useMemoeverywhere.
function Search({ items }) {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query); // typing stays instant
const results = useMemo(() => filter(items, deferredQuery), [items, deferredQuery]);
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ResultList results={results} />
</>
);
}Step 4: Cut presentation delay
After your code runs, the browser still has to render. Large DOMs and expensive CSS make that slow:
- Keep the DOM lean — thousands of hidden nodes in menus and tabs still cost style and layout time.
- Use
content-visibility: autofor long off-screen sections. - Animate
transformandopacity, notwidth,toporbox-shadow. - Be careful with heavy
backdrop-filterand large blurred elements on low-end devices.
Step 5: Watch the usual suspects in modern stacks
- 3D and canvas scenes running every frame can starve interactions — pause them when off-screen and lower quality on weak devices.
- Smooth-scroll libraries run code on every frame; make sure they're not fighting with your handlers.
- Tag managers accumulate tags over the years; audit and remove the ones nobody uses.
Why it matters beyond rankings
Core Web Vitals are part of Google's page experience signals, but the bigger win is commercial: a form that responds instantly, a menu that opens on the first tap and a checkout that never feels stuck convert better. Fix INP for your users; the search benefits follow.
_Want a performance audit with fixes implemented, not just a report? See our SEO service and web development._
Keep reading
All articles →Next.js 16 Cache Components in Practice: use cache, cacheTag and updateTag
How we rebuilt this site's CMS-driven pages on Next.js 16 Cache Components — instant static pages, content that updates the moment you hit save, and a database fallback that never breaks the site.
Prime Alley Microservices — One Product, Four Deployable Services
How we split billing, accounts, payments, and live updates into separate services while keeping a single login and navigation shell for end users.
SEO for AI Search: How to Get Your Brand Cited in AI Answers
AI Overviews and chat assistants now answer many searches directly. Here's how technical SEO, structured data and genuinely useful content help your pages get cited — without chasing gimmicks.