Skip to content

INP Explained: The New Metric to Watch

14/01/2026 3 min read

Google has replaced FID with INP (Interaction to Next Paint). Learn what this metric typically measures and how to optimize your main thread for responsiveness.

Performance metrics dashboard showing Interaction to Next Paint (INP) measurements and optimization strategies

“It’s not enough to load fast. You have to respond fast.”

We’ve spent years obsessing over LCP (Largest Contentful Paint)—how fast does the big image show up? But users care about what happens after the page loads.

You click a button. Nothing happens for 2 seconds. Then it jumps. That is rage-inducing.

To capture this frustration, Google introduced INP (Interaction to Next Paint), which officially replaced FID (First Input Delay) as a Core Web Vital in March 2024.

What is INP?

Interaction to Next Paint measures the time from when a user interacts (click, tap, key press) to when the browser is able to paint the next frame showing the visual feedback.

Crucially, unlike FID (which only measured the first input), INP considers all interactions during the entire lifespan of the page session. It usually reports the worst (or near-worst) latency you experienced.

A “Good” INP score is under 200 milliseconds.

The Main Culprit: The Blocked Main Thread

The browser’s “Main Thread” is a single-lane highway. It handles:

  1. Parsing HTML/CSS
  2. Executing JavaScript
  3. Listening for Inputs
  4. Painting pixels to the screen

If you have a giant JavaScript function taking 500ms to run, and the user clicks a button during that time, the browser cannot process the click or update the screen until that function finishes. The user sees a frozen UI.

How to Optimize INP: The Scheduler API

The old hacks involved setTimeout(..., 0). The modern web gives us the Prioritized Task Scheduling API.

1. scheduler.yield()

This is the game-changer. It allows you to pause a long task, let the browser handle user inputs or paint frames, and then immediately resume where you left off.

The Old Way (Blocking):

function processData(items) {
  items.forEach((item) => heavyCalculation(item)); // Blocks for 500ms
}

The New Way (Yielding):

async function processData(items) {
  for (const item of items) {
    heavyCalculation(item);

    // Yield to the browser to let it breathe/paint
    await scheduler.yield();
  }
}

By awaiting scheduler.yield(), you explicitly tell the browser: “I’m done for a split second; check if the user clicked anything, then come back to me.”

2. scheduler.postTask()

For more granular control, use postTask to assign priorities to different chunks of work.

// High priority: Do this immediately (e.g., input response)
scheduler.postTask(() => updateUI(), { priority: "user-blocking" });

// Background priority: Do this when idle (e.g., analytics)
scheduler.postTask(() => sendAnalytics(), { priority: "background" });

3. Immediate Feedback

When a user clicks “Add to Cart”, don’t wait for the API response to update the UI.

  1. Optimistic UI: Immediately show the spinner or “Added!” state.
  2. Defer Logic: Process the analytics tracking or heavy data normalization after the next paint.

Measuring INP

You can’t optimize what you can’t measure.

  1. Chrome DevTools: The “Performance” tab now has an “Interactions” track. It vividly shows you which interactions were slow and exactly which script blocked the thread.
  2. Web Vitals Extension: A browser extension that shows the live INP of the current page.
  3. RUM (Real User Monitoring): Tools like Vercel Analytics, Sentry, or Datadog will report real-world INP scores from your users.

Conclusion

INP forces us to stop treating the browser as an infinite resource. It prioritizes the feeling of speed over just the loading of data. Use scheduler.yield() to be a good citizen of the main thread.