INP explained: learn how to measure, debug, and improve your Interaction to Next Paint scores to fix sluggish user experiences in production apps.

I spent three days last month chasing a ghost in a React dashboard that felt "heavy" despite having decent scores on every other Core Web Vital. The culprit was a high Interaction to Next Paint (INP) score that only appeared under real-world conditions, proving that even with a solid React Performance Patterns You Actually Need in 2025 strategy, you can still hit a wall if you aren't watching the right metrics.
INP measures the latency of all interactions a user has with your page throughout its entire lifecycle. Unlike First Input Delay (FID), which only measures the very first click, INP looks at the worst (or near-worst) interaction. It captures the time from when the user clicks or taps until the browser actually paints the next frame.
Think of it as a three-part journey:
If your INP is above 200ms, your site feels sluggish. Above 500ms, it feels broken.

We initially tried to "fix" our INP by wrapping every event handler in useCallback and memo. It didn't work. In fact, it barely moved the needle because our issue wasn't component re-rendering—it was long-running JavaScript execution blocking the main thread.
We were triggering a heavy data-transformation function inside an onClick handler. Even if the component itself was memoized, the main thread was still locked for about 350ms while the browser processed the logic. We were trying to solve a execution-time problem with a rendering-time tool.
To fix INP, you need to stop blocking the main thread. Here is the workflow I use now:
You can't fix what you can't see. Open Chrome DevTools, go to the Performance tab, and record an interaction. Look for the red triangles in the top bar—these represent long tasks (anything over 50ms). If your interaction triggers a long task, that’s your bottleneck.
If you have a function that takes 200ms, you need to slice it. The easiest way is using scheduler.yield() or a simple setTimeout trick to let the browser breathe.
JAVASCRIPT// Before: Blocking the main thread const handleUpdate = () => { processData(); // Takes 200ms updateUI(); }; // After: Yielding to the browser const handleUpdate = async () => { await new Promise(resolve => setTimeout(resolve, 0)); processData(); updateUI(); };
By yielding, you allow the browser to process pending paints, keeping the UI responsive.
If you’re doing heavy calculations—like parsing large JSON objects or complex filtering—move that logic into a Web Worker. It’s a bit more boilerplate, but it completely removes that heavy computation from the main thread, making your INP scores drop significantly.

While chasing a "good" INP score is important, don't let it become a vanity metric. I’ve seen teams obsess over sub-100ms scores while ignoring the fact that their app's core functionality is still confusing. I usually find that when I focus on keeping the main thread clear, other things like Generics in TypeScript that actually pay off for your codebase start to matter more because the code becomes cleaner and easier to refactor without side effects.
Does hydration affect INP? Yes. If your app is still hydrating when the user clicks, the main thread is likely busy, which will inflate your INP. Ensure your bundle size is lean so hydration finishes faster.
Is INP only about clicks? No, it includes keyboard interactions and taps. Anything that triggers a state change and a subsequent visual update counts.
What if my INP is fine but the app still feels slow? Check your CSS. Sometimes the "Presentation Delay" is high because of complex layout shifts or heavy paint-inducing CSS properties.
I’m still not 100% satisfied with our current approach to tracking INP in a local dev environment—it’s notoriously difficult to simulate real-world CPU throttling accurately. We’re currently exploring more robust RUM (Real User Monitoring) tools to get a better picture of what our users are actually seeing. If you're just starting, don't over-engineer the measurement tools; start by manually profiling the most common interactions and breaking up those long tasks.
Image optimization that moves the needle is about more than just compression. Learn how to use modern formats and responsive delivery to slash LCP times.