Skip to content
Journal

Engineering

INP Is a Core Web Vital Now. Here Is What Actually Moves It

Headshot of Alex Fujimoto
Alex Fujimoto
March 12, 2024 · 3 min read

Today is the day: Interaction to Next Paint officially replaces First Input Delay as the responsiveness metric in Core Web Vitals. We have been preparing client sites for this since the change was announced last May, and the one-sentence summary of a year of that work is: FID was a gift, and the gift is over.

FID measured only the delay before the browser could start processing your first interaction. Not the processing itself, not the rendering, and nothing after the first input. Almost everyone passed. It was the participation trophy of performance metrics. INP measures the time from a user interaction to the next frame the browser paints, across effectively all interactions on the page, and reports one of the worst. Click a filter, tap a menu, type in a search box: if the page takes 600 ms to visibly respond, INP saw it, even if it happened five minutes into the session.

The threshold for "good" is 200 milliseconds. In our audits across client properties over the past year, sites that passed FID with scores in the teens have opened their first INP report to numbers in the 400s. If you have not looked yet, look today.

Where the milliseconds actually hide

After a year of INP remediation, the culprits are boringly consistent:

  • Long tasks from third-party scripts. Tag managers, chat widgets, and consent platforms doing main-thread work exactly when the user interacts.
  • Oversized hydration. Frameworks re-running component trees on interaction, especially state updates that re-render far more than they need to.
  • Synchronous layout thrash. Handlers that read layout, write styles, and read again, forcing multiple reflows before anything paints.
  • Doing everything before painting anything. The classic: an event handler that fetches, computes, updates analytics, and only then updates the UI.

The fix pattern that pays first

The highest-value technique is embarrassingly simple: paint first, work later. Give the user visible feedback in the same frame, then yield to the main thread before the heavy lifting.

button.addEventListener("click", async () => {
  button.classList.add("is-loading");   // paint feedback now

  await new Promise(r => setTimeout(r, 0)); // yield to next frame

  const results = applyFilters(state);  // heavy work after paint
  render(results);
});

There are more elegant primitives coming, scheduler.yield in particular, but a well-placed yield after the visual update fixes a shocking fraction of real-world INP failures today. The user does not need the work to be done in 200 ms. They need evidence the click worked.

Beyond that: break long tasks under 50 ms, defer third-party scripts until after first interaction where contracts allow, and profile with the actual Performance panel instead of guessing. In every engagement, one or two specific interactions account for most of the failing score. Find them, fix them, re-measure. It is plumbing, not rocket science.

Why we care beyond the ranking signal

The search ranking implications get the budget approved, so fine, lead with that in the steering meeting. But the real reason to care is that INP is the first lab-and-field metric that roughly corresponds to whether a site feels broken. Users do not experience your Lighthouse score. They experience tapping a button and wondering if it worked. Every study we have run with client analytics shows rage clicks and abandonment clustering exactly where INP fails.

We set our internal budget at 150 ms, tighter than Google's threshold, for the same reason we set every budget tighter than the pass line: entropy only runs one direction. Sites get slower by default. Budgets are how you fight the default.

Check your field data this week. The metric changed today. Your users' experience did not. That is precisely the problem.

Building something this could apply to?

We take on a small number of flagship projects each quarter.

Start a project