Reference guide · performance · Published 2026-08-15 · 3 min read

Improving INP (Interaction to Next Paint)

Improve your INP under 200ms: understand what the metric measures, break up long main-thread work, and load JavaScript only when interactions need it.

What INP measures

Interaction to Next Paint measures the longest time between a user interaction, a tap, click, keypress, or hover, and the next paint after it. Unlike FID, which only counted the input delay before event handlers ran, INP covers the whole interaction: the delay while the main thread is busy, plus the time the handler and everything it schedules take to repaint. Good is 200ms or less; needs improvement up to 500ms; poor above that. The threshold for Core Web Vitals is the 75th percentile of the slowest interactions, typically near the worst-performing page.

Main thread vs input delay

The dominant cause of poor INP is a busy main thread. The browser runs rendering and most JavaScript on a single thread. When it is occupied, every input event sits in a queue and waits. That waiting, called input delay, is the largest chunk of a slow INP.

The three concrete sources, in the order they appear:

SourceWhy it happensCut it with
Long tasksOne script run of 50ms or more blocks everythingSplit work, lazy script, defer
Third-party scriptsAnalytics, chat, widgets on every pageAsync load, block or delay them
Heavy handlersA click handler that does layout or network workMake handlers light, do work off the critical chunk

Fix the main thread first

  1. Do not ship render-blocking JavaScript. Any script you can load with defer or async moves its parse and execution off the critical path. The render-blocking resources page covers both attributes and when each is right.
  2. Break long tasks. A handler that processes a list, reflows, or fetches should yield to rendering with await new Promise(r => setTimeout(r, 0)) between chunks, or be moved to a Web Worker for pure computation.
  3. Delay third-party work. Analytics that run at window load can afford to wait for idle time. One rule: the third party responsible for the regression is the one that runs expensive layout or fetches megabytes into the page.

Lazy script strategies

Trim event listeners

Every listener attached at page set-up fires on its event even if it does nothing useful. Common patterns:

Attach handlers with { passive: true } when they do not call preventDefault, and remove them when the element leaves. For scroll and resize at least debounce or throttle to a frame.

The scheduler

The browser's scheduler can run long callback functions without yielding: setTimeout and requestAnimationFrame for the next frame, plus scheduler.postTask when that API is available and on a supported browser. The mental model worth keeping is the queue: input events wait behind every task, so a page that keeps its script work small and yields to the event loop will keep INP under 200ms even while scripting.

Need a website built, fixed, optimised, migrated or replaced?

This technical resource is written by CSMBAC, a small design and development studio. If you would rather hand the problem to a professional, the website service page explains how we build enquiry-ready websites.

Explore website services