Interaction to Next Paint (INP) replaced First Input Delay as a Core Web Vital in March 2024. It measures how long the page takes to visually respond to clicks, taps, and key presses across the whole visit. React apps often struggle with it because big re-renders block the main thread.
The thresholds
| INP | Rating |
|---|---|
| ≤ 200 ms | Good |
| 200–500 ms | Needs improvement |
| > 500 ms | Poor |
Measured at the 75th percentile of real user visits.
Step 1: Find the slow interaction
- Field data: Search Console's Core Web Vitals report, or the
web-vitalslibrary with attribution, which tells you which element and interaction was slow. - Lab: Chrome DevTools Performance panel. Record, interact, and look for long tasks.
import { onINP } from "web-vitals/attribution"; onINP((metric) => { console.log(metric.value, metric.attribution.interactionTarget); });
Step 2: Understand the three parts
INP = input delay + processing time + presentation delay.
- Input delay: main thread busy when the user clicks (hydration, third-party scripts).
- Processing: your event handler and React's render.
- Presentation: layout and paint of the result.
Step 3: Fix
Render less
- Move state down to the component that needs it.
- Split big components so a keystroke doesn't re-render the whole page.
- Memoise expensive children (
React.memo,useMemo) where profiling shows it helps.
Mark non-urgent updates as transitions
const [query, setQuery] = useState(""); const [isPending, startTransition] = useTransition(); function onChange(e: React.ChangeEvent<HTMLInputElement>) { setQuery(e.target.value); // urgent: update the input startTransition(() => { setFilter(e.target.value); // non-urgent: filter a big list }); }
Virtualise long lists
Rendering 5,000 rows on every filter change kills INP. Use a virtualised list.
Yield in long handlers
Break heavy work into chunks so the browser can paint in between, using scheduler.yield() where supported or a setTimeout fallback.
Ship less JavaScript
- Use Server Components so less code hydrates (App Router lessons).
- Lazy-load heavy widgets (maps, editors).
- Audit third-party scripts. They're often the biggest input-delay source.