Skip to main content
All fixes

Page speed, redirects, Core Web Vitals

Fix INP poor: get Interaction to Next Paint under 200ms

INP above 200ms means the page feels laggy on every click and tap. Profile main-thread work, break up long tasks, and defer non-critical JS to pass Core Web Vitals.

What's happening

Interaction to Next Paint replaced First Input Delay as a Core Web Vitals metric in March 2024. INP measures the worst (well, near-worst) latency from a user interaction — click, tap, keypress — to the next visual update on screen across the entire page session. Good INP is below 200ms, needs improvement is 200-500ms, and poor is above 500ms. Unlike FID, which only captured the first interaction's input delay, INP catches every interaction including processing time and presentation delay.

An INP score breaks down into three phases: input delay (time before the event handler starts running, usually because the main thread is busy), processing time (your event handler executing), and presentation delay (time from handler completion to next paint). Chrome DevTools' Performance panel now highlights long interactions with their full breakdown, and the new Interactions track shows every event with its INP contribution.

The most common cause of poor INP in 2026 is heavy React hydration on initial load combined with non-debounced state updates inside event handlers. A click handler that triggers a synchronous re-render of a 5000-node component tree easily blows past 500ms on a mid-tier Android phone.

Why it matters

INP is now part of the Core Web Vitals ranking signal. Pages that fail at the 75th percentile lose page experience rankings on mobile. Failing INP is more common than failing LCP or CLS — the HTTP Archive's CrUX dataset shows roughly a third of mobile origins still flunk the threshold.

User-facing, poor INP shows up as the page feeling unresponsive or sluggish. Buttons appear to do nothing, text fields lag behind keystrokes, modals take a beat to open. Conversion and engagement metrics drop accordingly — Google's own studies on the YouTube and Google Search apps showed measurable user-satisfaction lifts after INP improvements.

Common causes

  • Long tasks on the main thread (over 50ms) blocking event handlers from running.
  • Event handlers that synchronously re-render large React subtrees.
  • Third-party scripts (analytics, A/B testing, heatmaps) parsing and executing on every interaction.
  • Non-debounced input handlers firing expensive computations on every keystroke.
  • Hydration mismatches forcing client-side re-render of large server-rendered trees.
  • useState updates that trigger cascade re-renders without React.memo or useMemo.
  • Heavy work in microtasks (Promise chains) after the event handler returns but before paint.

Detect this on your site

Run a quick scan with the Speed Test. The tool surfaces this exact issue with the records and context needed to apply the fix below.

Open Speed Test

How to fix it

  1. 1

    Profile interactions in DevTools

    Open Chrome DevTools Performance panel, throttle CPU to 4x slowdown, and record a session that includes the slow interactions. The Interactions track shows each event's full breakdown — input delay, processing, presentation. Find the worst offenders ranked by total time.

  2. 2

    Break up long tasks with scheduler.yield()

    Long tasks (over 50ms) prevent the browser from responding to input. Inside expensive event handlers, await scheduler.yield() (Chrome 129+) or use setTimeout(fn, 0) to yield to the browser between chunks. The web-vitals attribution build identifies which scripts contain the worst long tasks.

  3. 3

    Defer state updates with startTransition

    Wrap non-urgent state updates in React.startTransition so React can interrupt the render to handle higher-priority interactions. Concurrent rendering means the input remains responsive even while a heavy list re-renders.

  4. 4

    Memoize expensive components

    Use React.memo on components that re-render frequently, useMemo for derived state, and useCallback for handlers passed to memoized children. The React Profiler in DevTools shows which components actually re-render on each interaction.

  5. 5

    Move heavy work off the main thread

    Image processing, large JSON parsing, or data transformation belongs in a Web Worker. Use Comlink to ergonomically call worker functions from the main thread without blocking it.

  6. 6

    Audit and defer third-party scripts

    Run Lighthouse's third-party scripts audit. Move analytics, chat widgets, and A/B testing tools to, or load them via Partytown so their JS runs in a Web Worker entirely off the main thread.

  7. 7

    Reduce hydration cost with Server Components

    In Next.js App Router, default to Server Components and only use "use client" for genuine interactivity. Less client-side JS means lower initial hydration cost and lower steady-state INP. Use dynamic imports for client components that aren't immediately interactive.

  8. 8

    Validate with PageSpeed Insights field data

    INP is measured at the 75th percentile across the entire session, so synthetic tests miss the worst cases. PageSpeed Insights' Origin Summary shows real INP from CrUX, broken down by interaction type (click, tap, keypress).

Example

// Bad: blocks the main thread for 200ms+
button.addEventListener("click", () => {
  const result = expensiveComputation(largeDataset);
  setState(result);
});

// Good: yield to the browser, use a transition
import { startTransition } from "react";

button.addEventListener("click", async () => {
  await scheduler.yield();
  const result = expensiveComputation(largeDataset);
  startTransition(() => setState(result));
});

Yield between chunks and mark non-urgent updates as transitions.

Frequently asked

Yes. INP is one of the three Core Web Vitals used in Google's page experience ranking signal since March 2024. Pages that fail at the 75th percentile in CrUX lose mobile SERP visibility.

INP captures every interaction's full latency including processing and presentation delay not just the first interaction's input delay. Most pages had FID under 100ms but INP regressions when measured properly.

Workers help when the main-thread bottleneck is computation. They don't help if the bottleneck is React rendering or DOM manipulation which must happen on the main thread.

Related fixes