Fixing Interaction to Next Paint (INP): finding the slow handlers
INP replaced First Input Delay as a Core Web Vital in 2024 and is much harder to pass. Most poor INP comes from a few slow interactions caused by heavy JavaScript on the main thread, and the fix is to find them first.

Interaction to Next Paint measures how long a page takes to visibly respond after a user taps, clicks or presses a key. It looks at interactions across the whole visit and reports roughly the worst one. If someone taps "Add to cart" and nothing happens for 600 milliseconds, that tap defines the page's INP, even if every other interaction was instant.
INP became a Core Web Vital in March 2024, replacing First Input Delay. FID only measured the delay before the first interaction's handler started; INP measures the full time to the next frame for every interaction. Many sites that passed FID easily fail INP, especially on mid-range Android phones. The threshold for "good" is 200 ms at the 75th percentile.
Why interactions get slow
Browsers run JavaScript, layout and painting on one main thread. When a user taps, the browser must wait for whatever is currently running on that thread, then run your event handlers, then recalculate styles and layout, then paint. Each of those steps can be slow, and they add up.
| Phase | What happens | Typical cause when slow |
|---|---|---|
| Input delay | The tap waits for the main thread to be free | Long tasks already running: hydration, third-party scripts, timers, analytics |
| Processing time | Your event handlers run | Heavy handlers: re-rendering large components, synchronous work, many listeners |
| Presentation delay | Style, layout and paint of the next frame | Huge DOM, complex CSS, layout thrashing, large re-renders |
A "long task" is any main-thread task over 50 ms. A page with frequent long tasks is like a shop counter where the assistant is always on the phone: customers wait no matter how fast the actual service is.
Step 1: find the slow interaction
Field data tells you that INP is poor but not which interaction. Narrow it down in this order:
- Search Console's Core Web Vitals report groups URLs with similar problems. Pick the group with the most poor URLs and open an example.
- Think like the users of that template. On a product page: choosing a size, opening the image gallery, adding to cart. On a listing page: opening filters, toggling a checkbox. On an article: opening the menu, dismissing the consent banner.
- Measure from real users if you can. The web-vitals library can report the INP value along with the target element and the interaction type, which turns "INP is 450 ms" into "the size selector on product pages is slow".
- Reproduce in DevTools: open the Performance panel, enable 4× or 6× CPU throttling to approximate a mid-range phone, record, and perform the suspected interactions. The Interactions track shows each one with its duration and phases.
Always throttle the CPU. A developer laptop is several times faster than the median phone, and interactions that feel instant on a desktop often take 400 ms on a real device.

Step 2: read the phase that is slow
Long input delay
If the interaction waited a long time before your handler even started, something else was hogging the main thread. In the Performance recording, look at what was running just before the interaction. Frequent culprits:
- Hydration in JavaScript frameworks: the page looks ready, but the framework is still attaching behaviour to the server-rendered HTML, in one long block. Taps during that window wait.
- Third-party scripts: tag managers firing many tags at once, chat widgets initialising, ad auctions, session-recording tools.
- Timers and polling that do heavy work every few hundred milliseconds.
Long processing time
Your own event handler is slow. Common patterns: a click that triggers a full re-render of a large component tree; synchronous calls to analytics that build big payloads; reading and writing layout properties in a loop; handlers attached at the document level that run expensive checks for every click on the page.
Long presentation delay
The handler finished quickly but the frame took a long time to paint. Usually the DOM is very large (tens of thousands of nodes), the change affected a large part of it, or CSS selectors and effects are expensive to recalculate. Opening a mega-menu that inserts several thousand nodes is a classic example.
Step 3: the fixes that work
Give the browser a chance to paint first
Do the minimum visible work in the handler (show a spinner, toggle the button state) and move the rest to a later task. Yielding to the main thread lets the browser paint the immediate feedback, which is what INP measures:
button.addEventListener('click', async () => {
button.classList.add('is-loading'); // visible feedback right away
await new Promise(r => setTimeout(r, 0)); // yield so the browser can paint
updateCartAndAnalytics(); // heavier work afterwards
});
Newer browsers offer scheduler.yield() for the same purpose with better scheduling; use it where available, with a setTimeout fallback.
Break up long tasks
Any task that loops over many items, such as filtering a product list in the browser or processing a large JSON response, can be split into chunks with yields between them. The total work is the same, but taps can be handled between chunks.
Reduce JavaScript at load
- Audit third-party tags: remove the ones nobody uses, delay non-essential ones until after the page is interactive or until the user's first interaction.
- Split bundles so each page loads only the code it needs.
- For frameworks, use partial or progressive hydration where the framework supports it, so only interactive components hydrate.
Keep the DOM lean
Very large DOMs make every style recalculation and layout slower. Render long lists progressively or virtualise them, avoid hidden duplicate navigation trees for mobile and desktop, and do not inject large blocks of markup on interaction when a simpler change would do. The CSS property content-visibility: auto can let the browser skip rendering off-screen sections.
Avoid layout thrashing
Reading a layout property such as offsetHeight after changing styles forces the browser to calculate layout immediately. Doing this repeatedly in a loop multiplies the cost. Batch reads first, then writes.
Consent banners and other first-interaction traps
On many sites the single slowest interaction is the very first one: accepting or rejecting the cookie consent banner. The click triggers the consent tool to record the choice, then fires every tag that was waiting for consent, all in the same task. Analytics, advertising pixels, heatmaps and chat widgets initialise at once, and the banner stays on screen until they finish. Users experience a button that does not respond.
The fix is the same pattern as above: hide the banner immediately, then release the waiting tags in a later task, ideally staggered. Most consent platforms have settings for this, and tag managers can delay non-essential tags until the page is idle. The same trap applies to newsletter pop-ups and "accept terms" overlays that unlock scripts when closed.
Platform-specific notes
- WordPress and page builders: INP problems usually come from plugins adding scripts to every page (sliders, popups, chat, analytics) and from builders that produce very deep DOMs. See speeding up WordPress.
- Shopify and hosted shops: apps inject scripts into the theme. Uninstalling an app does not always remove its code; check the theme files. See Shopify SEO.
- Single-page apps: route changes count as interactions. A navigation that rebuilds the whole page in one task will show up as a slow click.
Measuring the effect
Lab measurements confirm a fix quickly: record the same interaction with the same CPU throttling before and after. Field data then takes up to 28 days to reflect it in Search Console and PageSpeed Insights. Expect INP to move more gradually than LCP, because it depends on what users actually do, and the slowest interaction on one visit may be a different one on the next.
INP is one of three metrics; fixes for it can affect the others. Deferring scripts usually helps LCP too, while lazily inserting content can introduce layout shifts. The CLS guide covers what to watch for, and the LCP guide covers loading performance.
Checklist
- Pick the template group with the most poor URLs in Search Console.
- List the likely interactions on that template and reproduce them with CPU throttling.
- Identify the slow phase: input delay, processing or presentation.
- Input delay: reduce or delay third-party scripts, split hydration.
- Processing: give immediate feedback, yield, move heavy work later.
- Presentation: shrink the DOM, avoid large inserts, batch layout reads and writes.
- Re-test in the lab, then watch field data over four weeks.
Questions
What is a good INP?
200 milliseconds or less at the 75th percentile of real visits is good. Between 200 and 500 ms needs improvement, and above 500 ms is poor.
Why can't I see INP in Lighthouse?
INP needs real interactions, and a standard Lighthouse run does not click anything. Use field data from Search Console or PageSpeed Insights, or record interactions yourself in Chrome DevTools. Lighthouse's Total Blocking Time is a rough lab proxy.
Which interactions count?
Clicks, taps and key presses. Scrolling and hovering do not count. INP reports roughly the slowest interaction of a visit, ignoring a few outliers on pages with very many interactions.
Do third-party scripts affect INP?
Often heavily. Tag managers, chat widgets, ad scripts and analytics run on the same main thread as your own code. A long task from any of them can delay a user's tap.
Keep reading

Fixing Largest Contentful Paint (LCP): a step-by-step diagnosis
LCP measures when the biggest thing on screen appears. Every slow LCP breaks down into four phases, and once you know which phase is slow,…

Fixing Cumulative Layout Shift (CLS): the usual culprits
CLS measures how much visible content jumps around unexpectedly. Almost every layout shift comes from content arriving without reserved spa…

Speeding up WordPress without breaking it
Most slow WordPress sites are slow for the same four reasons: uncached pages on weak hosting, too many plugins loading assets everywhere, h…