Interaction to Next Paint (INP): What It Is, How to Measure and Improve It

What INP measures, how it differs from FID and which of the three phases to fix first.

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-10-07

Interaction to Next Paint (INP) is a Core Web Vital that measures how quickly a webpage responds to user interactions such as clicks, taps, and key presses. INP captures the full latency from user input through JavaScript processing to the final visual update on screen. A good INP score is 200 milliseconds or less at the 75th percentile. INP replaced First Input Delay (FID) as a Core Web Vital in March 2024.

What is Interaction to Next Paint (INP)?

INP is the Core Web Vital for responsiveness. It looks at every interaction during a visit and reports one number for the page.

On every click, tap or key press, the browser measures the time from the input to the next frame on screen. The slowest interaction becomes the INP of that visit. On pages where people click a lot, Chrome skips one outlier for every 50 interactions, which comes down to roughly the 98th percentile.

INP vs FID: What Changed and Why

INP officially replaced First Input Delay (FID) as a Core Web Vital in March 2024. Google retired FID because it missed two things.

First, FID only measured the first interaction on a page. If a user's first click was fast but subsequent interactions were slow, FID would report a passing score despite the poor experience. INP measures every interaction during the visit.

Second, FID only measured the input delay phase, meaning it captured the time the browser spent waiting before starting to process the event. It ignored the time spent running the event handlers (processing time) and the time needed to paint the result on screen (presentation delay). INP measures all three.

AspectFirst Input Delay (FID)Interaction to Next Paint (INP)
StatusRetired (March 2024)Active Core Web Vital
Interactions measuredFirst interaction onlyAll interactions throughout the session
Phases capturedInput delay onlyInput delay + processing time + presentation delay
"Good" threshold100ms200ms
Reporting methodDelay of the first inputSlowest interaction (one outlier skipped per 50 interactions)

Almost every site passed FID. Many of them fail INP, because their later interactions, or the work after the first click, are slow.

Which Interactions Does INP Measure?

INP only measures discrete interactions: a click, a tap or a key press.

Interactions that count toward INP

  • Mouse clicks (including clicks on buttons, links, checkboxes, and custom controls)
  • Taps on touchscreens (equivalent to clicks on mobile devices)
  • Key presses on a physical or on-screen keyboard (including typing in form fields, pressing Enter, using keyboard shortcuts)

Interactions that do NOT count toward INP

  • Scrolling (see below)
  • Hovering (mouseover)
  • Dragging, although the pointerdown that starts a drag can be measured
  • CSS transitions and animations that run without user input

Scrolling itself doesn't count. Scroll code can still hurt INP in two ways. A click on an anchor link that starts a JavaScript scroll animation is an interaction, and the scroll code runs inside it. And scroll handlers that run on every scroll event keep the main thread busy, so the next click has to wait. scroll-behavior: smooth in CSS avoids both.

The Three Phases of an INP Interaction

Every interaction measured by INP has three phases, one after the other (Google calls them subparts). The INP value is the sum of the three, and each phase needs a different fix.

inp breakdown

1. Input Delay

The input delay is the time between the click and the start of the event handlers. The click has to wait for whatever task is running on the main thread at that moment: a tag manager, a consent banner script, a framework that is still hydrating.

Input delay is worst while the page is still loading. In CoreDash data, clicks during loading are more than twice as slow as clicks after load (numbers further down).

2. Processing Time

The processing time is the time the event handlers for the interaction take to run: form validation, a state update, an analytics call.

If an event handler does expensive DOM work, synchronous API calls or slow loops, processing time goes up. A classic: a dataLayer.push() in the click handler, so every tag in the tag manager runs before the screen can update. The fix is to yield before the push.

3. Presentation Delay

The presentation delay is the time from the end of the event handlers to the next frame on screen: style, layout, paint.

Pages with a large or deeply nested DOM take longer to recalculate styles and perform layout. Single page applications that re-render large component trees after a state change often have a high presentation delay.

What Are Good and Bad INP Scores?

To pass, the INP of your page visits must be 200 milliseconds or less at p75:

  • An INP below or at 200 milliseconds means that your page has good responsiveness.
  • An INP between 200 and 500 milliseconds means that your page's responsiveness needs improvement.
  • An INP above 500 milliseconds means that your page has poor responsiveness.

interaction to next paint

INP uses the 75th percentile, so 75% of page visits need an INP under 200ms. That includes visitors on slow phones.

How to Measure Interaction to Next Paint (INP)

INP needs real visitors who click, tap and type, so the INP that counts comes from field data. A lab test that only loads the page has no INP. Lighthouse shows Total Blocking Time instead, which tells you how busy the main thread is during load, and nothing about the click on your filter menu.

Get the Official INP Metrics

The official INP comes from CrUX. PageSpeed Insights shows the 75th percentile of the last 28 days of Chrome data for a URL or origin. CrUX Vis shows how it changed over the last months, and CrUX on BigQuery has the monthly data for every origin.

Google Search Console groups your URLs in the Core Web Vitals report. A failing group shows up as "INP issue: longer than 200ms (mobile)" or "INP issue: longer than 500ms (mobile)", with example URLs. It covers the last 28 days, so two weeks after a fix goes live, half of what it shows is still the old site.

Track INP with Real User Monitoring

CrUX tells you that a page group fails. It doesn't tell you which button. For that you need Real User Monitoring. In CoreDash I look at INP p75 on mobile first, split by the element that was clicked and by load state.

Measure INP for the Current Session

Open the Performance panel in Chrome DevTools. It shows the INP of your current session as you click around, with a log of every interaction and its three phases. Record a trace while you click to see what ran on the main thread. The Core Web Vitals Visualizer extension shows the INP on the page itself.

For hands-on debugging, use the Google Web Vitals JavaScript Library to log individual interactions to the console:

Log the INP to the console with the Web Vitals JavaScript library

<script type="module">
 import {onINP}
 from 'https://unpkg.com/web-vitals@6/dist/web-vitals.attribution.js?module';
 onINP(console.log);
</script>

How to Improve Interaction to Next Paint

One slow interaction can set the INP for the whole visit, even when every other click is fast. Work through the three phases.

PageSpeed TIP: log the page load state with every interaction. A slow click during loading needs a different fix than a slow click on a loaded page.

1. Minimize Input Delay: Prevent Long Tasks on the Main Thread

Most main thread work happens during startup. Keep it free:

  • Remove unused code. The Coverage panel in DevTools shows how much of each script never runs. Tree shaking and code splitting take care of the rest.
  • Load non-essential code during browser idle time. Do you really need a chat widget during the first 500ms of page load? Schedule non-critical scripts with requestIdleCallback() to run only when the browser is idle.
  • Find the long task under the click. Record a trace in the Performance panel while you click during load. The task that runs when your click comes in is the one to fix first.
  • Use async and defer on script tags to prevent JavaScript from blocking the HTML parser. Scripts nobody needs in the first seconds can wait longer: 14 methods to defer JavaScript.
  • Skip the passive listener advice. { passive: true } is often recommended for INP. It makes scrolling smoother. INP doesn't care.

Break Up Long Tasks with scheduler.yield()

JavaScript runs on the browser's main thread using a "run to completion" model: once a task starts, it blocks the main thread until it finishes. Long tasks (over 50ms) prevent the browser from responding to user input. With scheduler.yield() you give the browser a moment to handle a waiting click before your code continues. More in yield to the main thread.

async function yieldToMain() {
  if ('scheduler' in window && 'yield' in window.scheduler) {
    return await window.scheduler.yield();
  }
  // Fallback for browsers without scheduler.yield()
  return new Promise((resolve) => {
    setTimeout(resolve, 0);
  });
}

// Usage: break up a long task into smaller chunks
async function processLargeDataSet(items) {
  for (let i = 0; i < items.length; i++) {
    processItem(items[i]);

    // Yield every 5 items to let the browser handle user input
    if (i % 5 === 0) {
      await yieldToMain();
    }
  }
}

Defer Non-Critical Work with requestIdleCallback

Use requestIdleCallback() to schedule non-essential tasks (analytics, telemetry, prefetching) for when the browser is idle. That keeps them out of the processing time of the click.

// Instead of running analytics synchronously:
document.querySelector('.cta-button').addEventListener('click', (e) => {
  // Critical: update the UI immediately
  showConfirmation();

  // Non-critical: send analytics during idle time
  requestIdleCallback(() => {
    sendAnalyticsEvent('cta_clicked', { page: location.pathname });
  }, { timeout: 2000 });
});

2. Minimize Processing Time: Provide Immediate Feedback

When a visitor performs an action like submitting a form or adding an item to a basket, don't wait for the server-side confirmation before updating the UI. Provide immediate visual feedback ("Submitting your form...", "Adding item to basket...") and then complete the operation in the background.

Also, yield to the main thread as soon as possible after the critical visual update. Because JavaScript follows a "run to completion" model, it blocks the main thread until all code in the callback has executed. You can manually create a yield point where the browser can update the layout and then continue running the remaining non-critical code.

const formfeedbackEl = document.getElementById("formfeedback");
const formEl = document.getElementById("form");

formEl.addEventListener("submit", (evt) => {
  evt.preventDefault();
  formfeedbackEl.innerText = "Submitting form ... please hold on";

  let headers = new Headers({ Accept: "application/json" });
  let formData = new FormData(formEl);
  fetch("/form-endpoint", { method: "POST", headers, body: formData })
    .then(function (response) {
      return response.json();
    })
    .then(function (jsonData) {
      formEl.reset();
      formfeedbackEl.innerText = jsonData.message;
    });
   setTimeout(other_code_that_needs_to_run, 0);
});

3. Minimize Presentation Delay: Keep Things Simple

When the page needs to update after an interaction, the browser must recalculate styles, perform layout, paint the changed pixels, and composite the result. The complexity and size of your DOM directly determines how long this rendering work takes.

Some single page applications re-render half the page after each interaction, when only a counter changed. React DevTools and React Scan show it. Often one context provider high up in the tree re-renders everything below it.

Two rules for faster rendering:

  1. Keep the DOM small and simple. A small, flat DOM renders faster than a large, deeply nested one. Lighthouse flags pages with more than 1,400 DOM elements or nesting deeper than 32 levels.
  2. Use content-visibility to lazy-render off-screen content. The content-visibility: auto CSS property speeds up initial rendering by deferring the rendering of off-screen content until the user scrolls near it.
/* Apply content-visibility to off-screen sections */
.below-the-fold {
  content-visibility: auto;
  contain-intrinsic-size: auto 500px;
}

Debugging INP with the Long Animation Frames (LoAF) API

The Long Animation Frames (LoAF) API gives you details about frames that take longer than 50ms to render, including which scripts ran during them.

Unlike the older Long Tasks API, LoAF captures rendering time as well as script execution time, making it particularly useful for diagnosing presentation delay issues. Each entry has the frame duration and the blocking duration. Its scripts list shows, per script, the source URL, the function name and how long it ran.

// Observe Long Animation Frames to find INP bottlenecks
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Only look at frames longer than 100ms
    if (entry.duration > 100) {
      console.log('Long frame:', entry.duration + 'ms');

      // Log each script that contributed
      for (const script of entry.scripts) {
        console.log(
          'Script:', script.sourceURL,
          'Function:', script.sourceFunctionName,
          'Duration:', script.duration + 'ms'
        );
      }
    }
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

RUM tools like CoreDash integrate LoAF data automatically, correlating long animation frames with specific user interactions so you can see which script on which page makes which interaction slow.

INP and LCP: The Long Task Connection

Long tasks on the main thread affect both INP and Largest Contentful Paint (LCP). A JavaScript task that blocks the main thread for 200ms delays event processing (worsening INP) and also delays the browser from rendering the LCP element (worsening LCP). When you optimize your main thread by breaking up long tasks, deferring non-critical JavaScript, and reducing script execution time, you improve both.

Case Studies: INP Improvements in Production

redBus: INP Down 72%, Sales Up 7%

redBus, an online bus ticketing platform, found slow interactions in their RUM data that they had not expected. On the search page, scroll handlers fired on every scroll event and kept the main thread busy, so they debounced them. On another page, every change in an input field ran the state reducer, so they called it less often. That page's INP improved 72%, and redBus saw sales go up about 7%. (redBus case study on web.dev)

Preply: INP from 250ms to 185ms on a Next.js Site

Preply, an online language tutoring platform on Next.js, brought the INP of their home page from about 250ms to 185ms and their search page to 175ms. They did it without React Server Components. They virtualized a long list, debounced keyboard events, memoized heavy components, stopped a tooltip from re-rendering the whole page and fixed the state handling of their privacy banner. Before they started, they estimated that getting both pages under 200ms could be worth about $200K a year in search traffic. (Preply engineering blog)

What CoreDash Data Shows About INP

The Mobile-Desktop INP Gap

In CoreDash data, mobile INP (131ms at p75) is 2.8 times desktop INP (48ms). Phones have slower CPUs. The same JavaScript blocks the main thread longer. On desktop 95.6% of interactions are good and on mobile 88.0%, and mobile has five times as many poor ones (9.6% against 1.9%).

Keyboard vs Pointer Interactions

CoreDash data shows that keyboard interactions (75ms p75) are 56% slower than pointer interactions (49ms p75). Keyboard events also have about five times as many poor experiences: 7.4% of keyboard interactions result in poor INP, compared to just 1.4% for pointer interactions. Keyboard events, especially in form fields, often trigger validation, autocomplete and state updates.

During-Load vs Post-Load Interactions

Interactions during page load have a much higher INP than interactions after the page has loaded. CoreDash data shows that interactions during the "loading" phase have a p75 INP of 132ms, compared to just 50ms for post-load interactions. That is a 2.6 times difference. Even interactions during the "dom-content-loaded" phase (75ms) show elevated INP because async scripts and sub-resources are still being processed. Less JavaScript during startup is the fix: defer what can wait.

INP Across the Web

In the CrUX release for August 2026, 85.3% of origins had a good INP, down from 87.2% in March. INP went down every month in between, and it is the only Core Web Vital that got worse compared to August 2025 (85.7%). The Chrome team calls it a cause for concern and has no explanation yet.

The 2025 Web Almanac shows the gap between devices: 97% of desktop pages have a good INP against 77% on mobile. The top 1,000 sites improved the most, from 53% to 63% good on mobile. Meanwhile the median mobile Total Blocking Time went up 58%, from 1,209 to 1,916 milliseconds.

Frequently Asked Questions

What is a good INP score?

A good INP is 200 milliseconds or less for at least 75% of page visits. Between 200 and 500ms needs improvement. Above 500 is poor. Google uses these thresholds in the Core Web Vitals assessment, and that assessment is one of the signals it ranks pages on.

What replaced First Input Delay (FID)?

Interaction to Next Paint (INP) replaced First Input Delay (FID) as a Core Web Vital in March 2024. FID only measured the input delay of the first interaction. INP measures every interaction during a visit, from the input to the next frame on screen.

Does scrolling affect INP?

No. INP only measures clicks, taps and key presses. Scroll code can still hurt it. A click on an anchor that starts a JavaScript scroll animation is an interaction. And scroll handlers that run on every scroll event keep the main thread busy when the next click comes in. CSS scroll-behavior: smooth avoids both.

What interactions does INP measure?

INP measures clicks, taps and key presses, including typing in a form field. INP does not measure scrolling, hovering, dragging, or CSS animations. For each interaction, INP captures the total time from user input through event handler processing to the next visual frame painted on screen.

Why is my INP worse on mobile?

Phones have slower processors and less memory than desktops. The same JavaScript takes longer on a phone, so the main thread is busy more often when the visitor taps. CoreDash data shows that mobile INP (131ms at the 75th percentile) is 2.8 times worse than desktop INP (48ms). To improve mobile INP, focus on reducing JavaScript execution time, breaking up long tasks, and minimizing DOM complexity.

Related Guides

Each part of INP has its own guide:

Related PageSpeed guides:

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

I built CoreDash for my own audits.

Under 1KB. EU hosted. No consent banner. Now with MCP support.

Try CoreDash free
Interaction to Next Paint (INP): What It Is, How to Measure and Improve It Core Web Vitals Interaction to Next Paint (INP): What It Is, How to Measure and Improve It