What is Interaction to Next Paint (INP)?
Interaction to Next Paint — INP — measures how long your page takes to respond after a user interacts with it. "Interacts" means clicks, taps, or key presses (not scrolls, not hovers). "Respond" means the browser has fully processed the event and painted the next frame.
INP replaced First Input Delay (FID) in March 2024 as an official Core Web Vital. FID only measured the first interaction — which was almost always fine, because the first click happens before most JavaScript loads. INP measures every interaction and reports the worst one the user experienced (roughly — it's technically the 98th percentile for pages with many interactions). That catches the real problem: the page gets sluggish after a bunch of stuff loads.
Thresholds
INP is measured in milliseconds, at the p75 of field data:
- Good: 200 ms or less
- Needs improvement: 200 to 500 ms
- Poor: over 500 ms
INP above 500ms feels broken. A user taps, nothing happens, they tap again. On slower devices you routinely see 1–3 second INPs on pages heavy with third-party JavaScript.
What gets measured
INP captures the total time from:
- The user interaction
- Through event-handler execution
- Through any synchronous work the handler triggers (React re-renders, layout work, etc.)
- Until the browser paints the next frame
If your click handler takes 50ms and then triggers a React re-render that takes 300ms, INP is 350ms+ — not 50ms.
Common causes
The same pattern every time: too much JavaScript running on the main thread.
- Large React component trees re-rendering on every state change
- Third-party scripts (analytics, ads, chat widgets, A/B testing) running long tasks
- Heavy event handlers (e.g., a filter change that recomputes a large list)
- Long-running timers and animation frames
- Synchronous localStorage / IndexedDB work inside handlers
Fixes
Break up long tasks. Any task over 50ms blocks input. Use scheduler.yield() (or a setTimeout(0) fallback) to yield to the main thread between chunks of work.
Move work off the main thread. Web Workers for CPU-heavy computation. OffscreenCanvas for rendering. requestIdleCallback for non-urgent stuff.
Audit third-party JavaScript aggressively. Every analytics script, chat widget, A/B tool, and ad network is a potential INP killer. Load them async at minimum, defer them if they can wait, remove them if they can't.
Reduce React re-renders. useMemo, useCallback, React.memo where the profiler shows wasted renders. Virtualize long lists. Split state so typing in one input doesn't re-render the whole page.
Use content-visibility: auto on below-the-fold sections so the browser doesn't waste work laying them out.
How to measure in the field
The web-vitals npm library will report INP events from your real users. Wire it into your analytics. You'll find the specific interactions that are slow, and it's almost always a handful of handlers that need to be rewritten.
Lab tools (Lighthouse) give you a synthetic interaction, which is useful for debugging but doesn't catch the real-world worst case. For INP specifically, trust the field data.