Interaction to Next Paint (INP): Definition, Thresholds, Fixes

INP measures page responsiveness — longest interaction → next paint across the lifecycle. Replaced FID 2024-03-12. ≤ 200ms = good.

What is Interaction to Next Paint (INP)?

Interaction to Next Paint (INP) is the Core Web Vital that measures a page's overall responsiveness to user interactions. For each click, tap, or key press, INP measures the time from the input event to the next visual update (next paint). The page's reported INP is essentially the LONGEST such interaction across the entire visit.

INP replaced First Input Delay (FID) as a Core Web Vital on March 12, 2024. FID only measured the very first interaction; INP samples interactions throughout the page lifecycle and reports the worst — which much better reflects how the page actually feels to use.

INP thresholds

INPRating
≤ 200msGood
200ms - 500msNeeds Improvement
> 500msPoor

Reported as 75th percentile across users — a page passes only if 75% of users have ≤ 200ms.

Why INP replaced FID

AspectFID (deprecated)INP
CapturesOnly first interactionAll interactions through page lifecycle
What it measuresInput delay onlyFull input → next paint duration
ReportingSingle value (first input)p98 of all (or worst, depending on count)
Good threshold≤ 100ms≤ 200ms
StatusDeprecated 2024Current Core Web Vital ✓

FID was easy to pass because most users had near-zero first-input delay. Many sites that "passed" FID still felt sluggish on subsequent interactions. INP catches that.

What INP includes

For each interaction, INP measures three sub-phases:

  1. Input delay — main thread busy time before event handler can run
  2. Processing time — event handler execution + state update + render-prep
  3. Presentation delay — time until browser paints the visual update

If any phase is slow, INP suffers. The browser pages: input → handler → state change → re-render → paint. INP captures end-to-end.

How to measure INP

JavaScript API

// Native PerformanceObserver — sample interactions
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.interactionId && entry.duration > 40) {
      console.log('INP candidate:', entry.duration, 'ms', entry.target);
    }
  }
}).observe({ type: 'event', buffered: true, durationThreshold: 40 });

// Or use the official web-vitals library
import { onINP } from 'web-vitals';
onINP((metric) => sendBeacon('/rum', metric));

Tools

ToolType
Web Vitals Chrome ExtensionLab + per-interaction
Lighthouse / PageSpeed InsightsField (CrUX) only — INP needs real interaction
Chrome DevTools PerformanceLab, frame-by-frame breakdown
web-vitals.jsRUM, programmatic
Search Console CWV reportField, your real users
CrUX DashboardField, public per-origin

Common INP optimizations

1. Break up long tasks

// Bad: blocks main thread for 200ms
for (const item of bigArray) processItem(item);

// Good: yield to browser between batches
async function processInBatches(arr) {
  for (let i = 0; i < arr.length; i += 100) {
    arr.slice(i, i + 100).forEach(processItem);
    await new Promise(r => setTimeout(r, 0));
  }
}

2. Defer non-critical JavaScript

<script src="analytics.js" defer></script>
<script src="chat-widget.js" async></script>

3. Use requestIdleCallback for low-priority work

requestIdleCallback(() => {
  prefetchNextPage();
  reportAnalytics();
});

4. Show feedback during slow operations

Even if you can't make the operation faster, optimistic UI updates make it FEEL faster. Show a loading spinner immediately on click; the actual processing can take longer.

5. Use Web Workers for CPU-heavy work

Move expensive computation off the main thread. Workers can't access the DOM but can do parsing, sorting, encryption.

6. Reduce React/Vue hydration cost

SSR + heavy hydration = first interactions are slow. Use partial hydration (Astro Islands), Server Components (React 18+), or resumability (Qwik).

7. Optimize event handlers

Debounce/throttle rapid events (scroll, input). Avoid layout thrashing (read-then-write DOM, not interleaved).

8. Reduce third-party scripts

Ad scripts + analytics + chat widgets compound. Each one adds main-thread time. Audit + cull.

Lab proxy: Total Blocking Time (TBT)

INP needs real user interactions, so synthetic Lighthouse can't measure it directly. Lab proxy is Total Blocking Time (TBT) — sum of main-thread blocking time over 50ms during page load. Strong correlation with INP.

TBTRating
≤ 200msGood
200ms - 600msNeeds Improvement
> 600msPoor

If TBT in CI is good but INP in field is bad, the issue is interaction handlers (post-load), not the load itself.

Common INP pitfalls

  • Long-running event handlers. Synchronous heavy work on click → INP spikes.
  • Hydration during user interaction. Click during hydration → input queued, late.
  • Layout thrashing. Read offsetHeight, then change DOM, then read again — forces multiple reflows.
  • Heavy hydration on slow devices. Mid-tier mobile takes 5x longer than desktop.
  • Synchronous network calls in handlers. Blocks until response.
  • Re-rendering huge component trees. Click in a virtualized list causes 1000 re-renders.
  • Third-party tag managers. Tag firing on click adds JS execution time.

FAQ: Interaction to Next Paint

What's a good INP score?

≤ 200 milliseconds at the 75th percentile. 200-500ms = needs improvement, > 500ms = poor.

How is INP different from FID?

FID measured ONLY the first interaction's input delay. INP measures ALL interactions, end-to-end (input → next paint), and reports the worst. INP catches sluggishness FID missed.

Why does my Lighthouse score look fine but field INP is bad?

Lighthouse can't measure INP directly (needs real interactions). Use Total Blocking Time (TBT) as a lab proxy. Field tools (CrUX, RUM) capture real INP.

How do I find which interaction is slow?

Web Vitals Chrome Extension shows the exact element + interaction. RUM tools (web-vitals.js, Datadog RUM) tag the slow handler.

Is INP the same as "page is laggy"?

Roughly. INP captures the worst-case responsiveness. A page with INP 50ms feels snappy; INP 800ms feels frustrating.

How does hydration affect INP?

Hydration is JavaScript executing — blocks the main thread. If user clicks during hydration, the click is queued and INP is high. Solutions: streaming SSR, partial hydration, server components.

What replaced FID — was it just INP?

Yes. INP became the official Core Web Vital responsiveness metric on March 12, 2024. FID is deprecated; use INP for all new audits.

Test INP-impacting JS bundles with LoadFocus

LoadFocus runs Lighthouse audits + load tests from 25+ global regions, measuring TBT (the INP lab proxy) and surfacing main-thread blocking work. Sign up free at loadfocus.com/signup.

How fast is your website?

Elevate its speed and SEO seamlessly with our Free Speed Test.

Free Website Speed Test

Analyze your website's load speed and improve its performance with our free page speed checker.

×