# INP explained: how to find and fix slow interactions

> INP measures how fast a page responds to clicks, taps and key presses. Learn what slows it down, how to find slow interactions, and how to fix them.

Published: 2026-11-15 · Author: AiroPages

Interaction to Next Paint (INP) measures how long a page takes to show a visual response after someone clicks, taps or presses a key, and reports one of the slowest interactions of the visit. A good INP is 200 ms or less. Most slow interactions come from long JavaScript tasks, and the fix is to do less work on the main thread, or to split it up so the browser can paint in between.

## What INP measures

[INP](/glossary/inp) times every click, tap and key press during a visit, from the moment of input until the browser paints the next frame. Scrolling and hovering are not counted. At the end of the visit, INP reports the slowest interaction, or close to it on pages with many interactions, where a few extreme outliers are ignored.

Each interaction has three parts:

1. **Input delay.** The time between the input and the moment the event handler starts. It is long when the main thread is busy with something else, such as a script still running from page load.
2. **Processing duration.** The time the event handlers themselves take to run.
3. **Presentation delay.** The time from the handlers finishing until the next frame is painted, including style calculation, layout and paint.

Knowing which part is large tells you where to look. A long input delay points at other work on the main thread. Long processing points at your handler. Long presentation delay points at a large DOM or expensive rendering.

## The thresholds

INP became a [Core Web Vital](/glossary/core-web-vitals) in March 2024, replacing First Input Delay. Like the other vitals, it is assessed on real users at the 75th percentile over a rolling 28 days.

| Rating | INP (75th percentile) |
|---|---|
| Good | 200 ms or less |
| Needs improvement | 200 ms to 500 ms |
| Poor | Over 500 ms |

The 75th percentile matters. A page that responds quickly on a fast laptop can still fail if a quarter of visits come from mid-range phones where the same JavaScript runs several times slower.

## INP is field-only, so use TBT and long tasks in the lab

INP needs real interactions, so it comes from field data such as the [Chrome UX Report](/glossary/crux). A standard Lighthouse run loads the page without touching it, so it cannot report INP.

The closest lab signal is [Total Blocking Time](/glossary/tbt). A long task is any main-thread task that runs for more than 50 ms. TBT adds up the part of each long task beyond 50 ms during page load. A page with high TBT has a busy main thread, and a click that lands during that time waits. TBT is also the largest single weight in the Lighthouse performance score, at 30%, as covered in [how the Lighthouse performance score is calculated](/blog/how-the-lighthouse-performance-score-is-calculated).

TBT and INP are related but not the same:

- TBT only covers page load. INP covers the whole visit, including a filter click two minutes later.
- TBT measures blocking in general. INP measures the specific interactions people actually use.
- A page can have low TBT and poor INP if one particular handler is slow, such as a search box that re-renders a large list on every key press.

Use TBT to catch heavy pages across the site, and field INP to confirm what users feel. [Lab data vs field data](/blog/lab-data-vs-field-data) goes deeper into why the two sources differ.

## How to find slow interactions

Work from the field data down to the code.

1. **Find the pages.** PageSpeed Insights shows field INP for a URL or origin when Chrome has enough data. Search Console's Core Web Vitals report groups similar URLs with INP issues.
2. **Reproduce in DevTools.** Open the page, set CPU throttling to 4x or 6x slowdown to approximate a phone, and use the Performance panel. It shows INP live as you interact, and a recording shows the long tasks behind each slow interaction.
3. **Try the likely interactions.** Menus, filters, add-to-cart buttons, accordions, tabs, form fields and search boxes are the usual suspects. Also click during load, since that is when input delay is worst.
4. **Collect real-user detail.** The attribution build of Google's `web-vitals` library reports the element interacted with and the input delay, processing and presentation split for slow interactions, so you can see which control is slow for real visitors.

## Common causes of slow interactions

- **Heavy JavaScript during load.** Large bundles parse and execute in long tasks. Any interaction that arrives during that time gets a long input delay.
- **Third-party scripts.** Tag managers, analytics, A/B testing, chat widgets, ad scripts and session recording all run on the same main thread as your code, and they often attach their own handlers to clicks.
- **Hydration.** Server-rendered pages built with a JavaScript framework can look ready before they are. Until hydration finishes, clicks either do nothing or wait behind a long task.
- **Expensive event handlers.** A handler that filters thousands of items, recalculates layout or sends synchronous analytics before updating the screen makes processing duration long.
- **Large DOM.** Every update to a page with tens of thousands of elements costs more style, layout and paint time, which shows up as presentation delay.
- **Layout thrashing.** Reading layout values such as `offsetHeight` right after changing styles, in a loop, forces the browser to recalculate layout repeatedly.

## How to fix INP

### Break up long tasks and yield to the main thread

The most effective change is to stop running work in one long block. Do the visible part first, then give the browser a chance to paint, then do the rest.

```js
function yieldToMain() {
  if (globalThis.scheduler?.yield) {
    return scheduler.yield();
  }
  return new Promise((resolve) => setTimeout(resolve, 0));
}

filterButton.addEventListener('click', async () => {
  showLoadingState();      // visible feedback first
  await yieldToMain();     // let the browser paint
  await applyFilters();    // heavy work afterwards
  await yieldToMain();
  sendAnalytics();         // non-urgent work last
});
```

`scheduler.yield()` is available in recent versions of Chrome; the `setTimeout` fallback works everywhere. Apply the same pattern inside long loops: process a batch, yield, continue.

### Defer and trim third-party scripts

- Remove tags nobody uses. Old tracking pixels and test scripts tend to pile up in tag managers.
- Load non-essential scripts after the page is interactive, or only when needed, for example a chat widget that loads when someone clicks the chat button.
- Load scripts only on the pages that need them, rather than site-wide.

### Reduce DOM size and rendering cost

- Paginate or virtualize long lists so only visible items are in the DOM.
- Use `content-visibility: auto` on long, off-screen sections so the browser skips rendering them until they are needed.
- Keep the part of the page that changes after an interaction small, so each update touches fewer elements.

### Debounce and throttle frequent handlers

For search-as-you-type, wait until typing pauses, often 150 to 300 ms, before filtering or fetching. Throttle handlers for resize and pointer movement. Update the input itself immediately so typing feels instant, and defer the expensive part.

### Make hydration cheaper

Ship less JavaScript to the page. Hydrate interactive parts only, rather than the whole page, and delay hydration of components below the fold. Code-split routes so a blog post does not load the checkout bundle.

## Cause and fix by phase

| Phase | Typical cause | First fix to try |
|---|---|---|
| Input delay | Long tasks from load, hydration or third parties | Split and defer scripts, load third parties later |
| Processing duration | Heavy work in the event handler | Show feedback, yield, then run the rest |
| Presentation delay | Large DOM, big re-renders, forced layout | Shrink the DOM, `content-visibility`, avoid layout thrashing |

## How to check this across your whole site

Manually, start with field INP in PageSpeed Insights or Search Console to find poor pages, then test their main interactions in DevTools with CPU throttling on. For a wider net, run Lighthouse on a sample of pages from each template and sort by TBT, since a heavy main thread on one product page usually means the same on all of them. [Lighthouse only tests one page](/blog/lighthouse-only-tests-one-page) explains why sampling just the homepage hides these problems.

AiroPages runs Lighthouse on every page on mobile and desktop, groups long-task and JavaScript issues by template, and shows Chrome UX Report field data next to the lab scores where Google has it, so you can see TBT and real-user INP side by side.

## Responsiveness is a main-thread budget

INP comes down to one question: is the main thread free when someone interacts? Load less JavaScript, split up the work you keep, give feedback before doing heavy tasks, and keep the DOM small. Then check field INP again after 28 days, because that is the window Google uses to judge the result.
