Skip to content
asim.dev
Sep 2, 20266 min read

Taming Interaction to Next Paint (INP): Profiling Real Pages, Not Lab Pages

What moving from lab Lighthouse runs to field INP data taught me about interaction cost on content-heavy storefronts.

Profiling and optimizing Interaction to Next Paint performance metrics in React

Lab scores lie politely. A Lighthouse run against a dealer storefront with two inventory cards will hand you a green 98 and a pat on the head. Real users open the page with 40 vehicles, a map embed, a tag manager and a 4G connection — and their click is what pays for all of it. INP measures that payment: the latency from interaction to the next visual change. Working on production storefronts at Revnix taught me more about it than any sandbox ever did.

Find the long handlers before users find them

The first honest step is field data. The web-vitals attribution build tells you which element and which phase (input delay, processing, presentation delay) is eating the budget:

ts
import { onINP } from "web-vitals/attribution";

onINP((metric) => {
  const { interactionTarget, inputDelay, processingDuration } = metric.attribution;
  reportToAnalytics({
    element: interactionTarget,
    inputDelay,        // main thread was busy *before* the click
    processingDuration // the handler itself
  });
});

In our case the split was telling: input delay dominated on pages where hydration and third-party tags owned the main thread. The handler wasn't slow — the queue was.

What actually moved the number

  • Breaking filters into steps. One mega-handler recomputing a full inventory grid became: update state, yield, compute, paint. Same result, a fraction of the blocking window.
  • Optimistic UI for cheap wins. Toggle states (favorites, compare) paint instantly from local state and reconcile with the server after — the user's click never waits on a request.
  • Scheduling the heavy stuff. Non-critical work (map markers, analytics enrichment) moved behind requestIdleCallback and startTransition so interactivity keeps its slot.
  • Auditing third-parties like code. Every tag-manager script got a budget conversation. Some lost.

The uncomfortable lesson

Most INP problems I've shipped were not algorithmic — they were architectural laziness: doing too much in one handler because it was the convenient place to do it. Profiling field data monthly keeps that honest in a way no CI check quite manages. The budget that holds is the one you keep measuring.