skip to content

INP breaks a single interaction's latency into input delay, processing time, and presentation delay. What happens during each phase, and what typically makes each one slow?

level: middleimportance: must knowfreq 80%

answer

  1. three consecutive slices, one timeline
  2. waiting, running, rendering
  3. input delay = main thread busy
  4. presentation delay ends at the paint
  5. fix depends on which slice dominates

basics

~20 s

Input delay is the wait before any handler runs, usually because the main thread is busy. Processing time is the event handlers themselves. Presentation delay is the styling, layout and paint work needed before the next frame shows the result.

solid answer

~50 s

An interaction's latency splits into three consecutive phases. **Input delay** runs from the user's physical input until the first event handler starts — time lost because the main thread was still occupied by another task, a timer, a third-party script, or startup work. **Processing time** is all the event listeners for that interaction running to completion, so it grows with expensive handler logic and with the number of listeners attached to the same event. **Presentation delay** covers everything after the handlers return: recalculating styles, layout, paint and compositing for whatever the handlers changed, plus waiting for the next frame to be presented. Fixes differ by phase — input delay is fixed by keeping the main thread free, processing time by doing less work or yielding inside the handler, presentation delay by changing less of the DOM and keeping the rendering work small.

code

javascript · 18 lines
javascript
function reportPhases(entry) {
  return {
    inputDelay: entry.processingStart - entry.startTime,
    processing: entry.processingEnd - entry.processingStart,
    presentation: entry.startTime + entry.duration - entry.processingEnd,
    total: entry.duration,
  };
}

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.interactionId) {
      console.table(reportPhases(entry));
    }
  }
});

observer.observe({ type: 'event', buffered: true, durationThreshold: 16 });

go deeper

for a junior

Name the three phases in order and say what each covers: waiting to start, handlers running, and rendering the result before the next frame.

for a middle

Explain the cause behind each phase — a busy main thread, expensive or numerous listeners, and heavy style/layout/paint work — and match a plausible fix to each.

for a senior

Demonstrate that you diagnose before you optimise: pull the phase breakdown from field data, identify the dominant slice, and resist the reflex to profile only your own handler.

for a principal

Frame it as a budget the whole page shares — third-party tags and startup work consume input delay that feature teams cannot recover, so responsiveness targets need owners for competing main-thread work, not just for handlers.

## Why the split matters "Our INP is 480 ms" is not actionable on its own. The three-phase decomposition turns one number into a diagnosis, because each phase has a different cause and a different fix. Two pages with identical INP can need entirely opposite changes. The phases are consecutive slices of one timeline: input → first handler starts → last handler ends → next frame painted. ## Phase 1 — input delay From the moment the browser receives the input to the moment the first event handler for that interaction starts executing. JavaScript is single-threaded on the main thread, and a task already running cannot be interrupted. If the user taps while a 300 ms task is executing, the browser cannot dispatch the event until that task finishes, and 300 ms of input delay is already spent before any of your handler code runs. Typical sources: - Long tasks from application startup, hydration or a big data transform. - Timers and interval callbacks that fire on a schedule regardless of the user. - Third-party scripts — tag managers, A/B testing, analytics — running work you did not author. - Rendering work for an unrelated update that was already queued. Input delay is the phase the user experiences as "nothing happened when I pressed it", and it is often worst right after load, when the page looks ready but is still executing script. ## Phase 2 — processing time All of the registered event listeners for the interaction running, in order. Note the plural: a click involves `pointerdown`, `pointerup` and `click`, and every listener bound to each of those events contributes. A page that registers ten listeners on the same button pays for all ten. What makes it slow: - Synchronous work with data-dependent cost — filtering or sorting a large collection, building a big DOM subtree, parsing a large payload. - Reading layout properties in a loop while also writing to the DOM, forcing the browser to recompute geometry repeatedly. - Writing to synchronous storage, or serialising large objects, inside the handler. - Several independent subscribers all reacting to the same event. Counterintuitively, processing time is often the *smallest* of the three phases in real field data. Teams that only optimise their handler are frequently optimising the wrong slice. ## Phase 3 — presentation delay From the last handler returning to the frame actually appearing. The browser must recalculate styles for what changed, run layout, paint, and composite — and it has to fit that into the next frame. If the handlers dirtied a very large part of the page, this work can dwarf the handler itself. What makes it slow: - Rendering a large list or table in one go instead of a page of it. - A change that invalidates layout across a large subtree rather than a contained region. - Style recalculation over a huge DOM because a class was toggled high in the tree. - Work queued to run right after the handler — microtasks or animation-frame callbacks — that extends the frame before it can be presented. ## Reading the phases in practice The Event Timing API exposes the timestamps directly, and the arithmetic is straightforward: ```js new PerformanceObserver((list) => { for (const e of list.getEntries()) { if (!e.interactionId) continue; const inputDelay = e.processingStart - e.startTime; const processing = e.processingEnd - e.processingStart; const presentation = e.startTime + e.duration - e.processingEnd; console.log({ inputDelay, processing, presentation, total: e.duration }); } }).observe({ type: 'event', buffered: true, durationThreshold: 16 }); ``` Field-monitoring libraries report the same three durations for the interaction that determined the page's INP, which is what makes the metric debuggable from production rather than only from a local reproduction. ## Matching fix to phase - **Input delay dominant** → the page is doing something else when the user acts. Cut or defer the competing work, delay non-essential third parties, break up startup tasks. - **Processing dominant** → the handler does too much. Do the minimum needed to update the screen, defer the rest until after the visual response, and drop redundant listeners. - **Presentation dominant** → the handler asks for too much rendering. Update a smaller region, render fewer nodes, and contain the layout impact of the change. A final subtlety: the user's perception ends at the paint, so the fastest possible response is often to paint a cheap acknowledgement first — a pressed state, a spinner, a disabled button — and do the expensive part afterwards. That does not make the work cheaper; it makes the *interaction* fast, which is exactly what INP measures.

  • Which of the three phases is usually the smallest in real field data, and why does that surprise people?
    Processing time is often the smallest. Teams instinctively profile their own handler because that is the code they own, but the interaction frequently spends more time waiting for a busy main thread beforehand and rendering the result afterwards. Measuring all three phases prevents optimising the slice that was never the problem.
  • Why can painting a cheap acknowledgement first improve INP even though the total work is unchanged?
    Because INP stops at the next paint after the interaction is handled. If the handler applies a pressed state or spinner and defers the expensive update, the frame that answers the user arrives quickly and the measured latency is short. The heavy work still runs, but it no longer sits inside the measured interaction.
  • A button has several independent listeners attached by different modules. How does that affect the phases?
    It inflates processing time: every listener for every event in the interaction runs before the phase ends, so ten subscribers each costing five milliseconds add fifty milliseconds. It also raises the chance that one of them dirties a large region and pushes presentation delay up as well.
  • Why is input delay often worst in the seconds right after a page appears to be ready?
    Because the page looks interactive before it is. Startup scripts, framework bootstrapping and third-party tags are still executing long tasks, and an input arriving mid-task cannot be dispatched until that task ends. The visual readiness of the page and the availability of the main thread are different things.

saying these in an interview costs you the question

  • Treats INP as only the time the handler runs
  • Says input delay is network latency
  • Assumes optimising the handler always fixes INP
  • Ignores rendering cost after the handler returns
  • Thinks presentation delay means waiting for a server response

context