Interaction to next paint (INP) optimization: INP optimisation for 2026

Interaction to next paint (INP) optimization: INP optimisation for 2026

Last verified: October 12, 2026
16 min read
Guide
Core Web Vitals
Technical SEO

In 2026, the definition of a "fast" website has moved beyond how quickly the first image appears. Today, speed is also defined by responsiveness.

When a user clicks a button on your WordPress site, does it react instantly, or is there a perceptible “hiccup”? That gap, the time between the user’s action and the browser’s visual response, is what Interaction to Next Paint (INP) measures. INP is one of the three Core Web Vitals, and Google Search states that Core Web Vitals “are used by our ranking systems”. The same documentation adds that there is no single page experience signal and that relevance comes first, so INP is a tiebreaker among useful pages rather than a shortcut past them. The stronger reason to fix it is the visitor: a slow menu or a frozen “Add to cart” button is felt on every page view.

This guide, a companion to our Core Web Vitals 2026 guide, covers how INP is calculated, what slows each phase of an interaction on a typical WordPress site, how to find the script responsible with Long Animation Frames, and the WordPress APIs that help.


#What is INP and when did it replace FID

INP became a stable Core Web Vital on 12 March 2024, replacing First Input Delay (FID). The web.dev launch post gave developers until 9 September 2024 to move any code that read FID from the CrUX or PageSpeed Insights APIs, after which FID availability was no longer guaranteed. PageSpeed Insights now evaluates INP instead of FID in its Core Web Vitals assessment.

The difference between the two metrics is scope. FID measured only the input delay of the first interaction on a page. INP observes the full latency (input delay, processing and presentation) of every click, tap and key press during the whole visit, so a page that loads well but chokes on its third interaction no longer passes.


#How is INP calculated

The web.dev definition of INP sets three bands, assessed at the 75th percentile of page loads and split by mobile and desktop:

INP valueRating
200 ms or lessGood
Over 200 ms and up to 500 msNeeds improvement
Over 500 msPoor

For each page view, the browser records the latency of every observed interaction. The reported INP is the longest one, with one rule for very busy pages: one highest interaction is ignored for every 50 interactions. Most page views have fewer than 50 interactions, so in practice the worst interaction usually becomes the INP of that visit. If you collect INP yourself in JavaScript, web.dev suggests taking the 98th percentile of the interactions on page unload, which approximates the same rule.

Only mouse clicks, touchscreen taps and key presses count. Hovering, scrolling and zooming are not observed, although a click or key press inside those gestures can be. That has a practical consequence: a page can have no INP value at all. It happens when the visitor never clicks, taps or types, when they only scroll, or when the visit comes from a bot such as a search crawler that was not scripted to interact. A blog post where most readers only scroll will often have thinner INP data than a product page with a variant picker.


#What are the three phases of INP

Every interaction measured by INP is the sum of three parts, as described in the web.dev guide to optimizing INP:

  1. Input delay: from the moment the user starts the interaction until the first event handler callback starts running. It grows when the main thread is busy with something else, such as a script being parsed, a timer firing, or a previous interaction still being handled.
  2. Processing duration: the time all event handler callbacks for that interaction take to run.
  3. Presentation delay: from the end of the callbacks until the browser paints the next frame, which includes style recalculation, layout and paint.

To reach a “Good” rating, each interaction, from a click on a navigation menu to a key press in a search field, has to complete visually in 200 ms or less. Fixing one slow interaction often exposes the next one, so treat INP optimization as an iterative loop rather than a single task.


#How to reduce input delay in WordPress

Input delay is usually caused by the main thread being busy when the user tries to click. On WordPress sites, the most common sources are script evaluation during load (parsing, compiling and executing plugin and theme JavaScript) and third-party scripts that run periodic work without regard for user input.

#How to yield to the main thread with scheduler.yield()

The web.dev article on long tasks defines a long task as any task over 50 ms. The fix is to split long JavaScript work into smaller tasks so the browser can handle an interaction in between. scheduler.yield() returns a promise that resolves in a new task, and its continuation is prioritized: the rest of your work resumes before other queued tasks of the same kind, so a third-party script cannot jump in between your chunks.

Support matters here. scheduler.yield() ships in Chrome 129, Edge 129 and Firefox 142, and Safari does not support it. web.dev recommends a fallback to setTimeout(), which still yields, only without the prioritized continuation:

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

async function processLargeList(items) {
  for (const item of items) {
    expensiveOperation(item);
    await yieldToMain();
  }
}

Older tutorials gate yielding on navigator.scheduling.isInputPending(). web.dev no longer recommends that API: it can return false right after a user interaction, and input is not the only reason to yield, since animations and other rendering updates also need the main thread. Yield regardless of pending input.

#How to remove unused JavaScript in WordPress

Only the JavaScript needed for the current view should load. If a user is on a blog post, there is no reason for a checkout library to be on the page. Conditional asset loading, by template or by block, is the first thing we check in a WordPress INP audit, because code that never loads can never block an interaction. More on this in our guide to WordPress speed optimization.

#How to defer scripts with the WordPress 6.3 loading strategy

WordPress 6.3 added a loading strategy to wp_register_script() and wp_enqueue_script(), documented in the Make WordPress Core note on async and defer. The fifth parameter now accepts an array with two keys, strategy (defer or async) and in_footer:

wp_enqueue_script(
  'wppoland-filters',
  get_theme_file_uri( 'assets/filters.js' ),
  array(),
  '1.4.2',
  array(
    'strategy'  => 'defer',
    'in_footer' => true,
  )
);

The strategy is a request, not a guarantee. WordPress checks the dependency tree: a deferred script whose dependent is a blocking script becomes blocking too, to keep execution order. An inline script added with the after position also forces its parent script back to blocking, and that change can cascade through the tree. So after switching a theme or plugin to defer, view the page source and confirm the attribute is actually there. The same note recommends replacing old script_loader_tag filter hacks with this API.


#How to reduce INP processing time

This is where the efficiency of your own code is tested. web.dev’s general advice is to do as little work as possible in event callbacks.

#How to keep event handlers light

Avoid complex logic inside click handlers. Update the part of the interface the user is waiting for, then yield, and run everything else in a later task. The web.dev example uses a text box: the typed character is shown at once, while word count, spell check and saving are deferred with requestAnimationFrame() and a setTimeout() inside it, so they run after the next frame is painted.

button.addEventListener('click', () => {
  const price = calculatePrice(product, variant);
  priceElement.textContent = formatPrice(price);

  requestAnimationFrame(() => {
    setTimeout(() => {
      sendAnalytics('price_calculated', price);
      saveCartDraft(product, price);
    }, 0);
  });
});

The visible change happens in the handler, and analytics and storage wait until the frame with the new price has been painted.

#How to avoid layout thrashing

Layout thrashing, also called forced synchronous layout, happens when code changes styles and then reads a layout property such as offsetWidth in the same task. The browser has to run layout immediately instead of once at the end of the frame, and in a loop that happens on every iteration. Read all values first, then write:

// Forces layout on every iteration: avoid
elements.forEach((el) => {
  const width = el.offsetWidth;
  el.style.width = width + 10 + 'px';
});

// Reads first, then writes
const widths = elements.map((el) => el.offsetWidth);
elements.forEach((el, i) => {
  el.style.width = widths[i] + 10 + 'px';
});

#How to move third-party scripts off the main thread

Non-essential third-party scripts, such as chat widgets and analytics tags, can be moved to a Web Worker with a library like Partytown, so they no longer compete with event handlers on the main thread. It does not suit every script, because some tags need synchronous access to the DOM. We cover the trade-offs, including which tags break in a worker, in our guide to reducing third-party script impact on WordPress.

#How to handle long lists

For pages with long lists, such as blog archives, WooCommerce category pages or data tables, rendering hundreds of items up front makes every later interaction pay for a large DOM. Virtualization renders only the items near the viewport and creates and removes elements as the user scrolls. Pagination or “load more” buttons reach the same goal with less JavaScript.


#How to fix presentation delay

Even if your code finishes quickly, the browser can still struggle to paint the result when the DOM is large or styles are expensive to recalculate.

#How DOM size affects INP

web.dev notes that rendering work grows with DOM size, though not linearly, and that a large DOM slows both the first render and rendering after each interaction. Two options are to flatten the DOM, which on WordPress usually means fewer wrapper elements from the theme or page builder, and to add nodes only when an interaction needs them instead of rendering every tab, modal and mega menu up front.

#How to use content-visibility and contain

content-visibility: auto lets the browser skip layout and paint for an element until it nears the viewport. According to the web.dev article on content-visibility, the travel blog demo dropped its initial rendering time from 232 ms to 30 ms, and the property has been Baseline newly available since 15 September 2025 (Chrome 85, Firefox 125, Safari 18). Pair it with contain-intrinsic-size so skipped sections keep a placeholder height; the auto keyword makes the browser remember the last rendered size:

.comments-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 2000px;
}

.sidebar {
  contain: layout style paint;
}

The contain property on independent sections, like a sidebar or a comment block, tells the browser that a change inside them will not affect the rest of the page, which limits how much of the page has to be recalculated before the next paint.

#Why client-side rendering of HTML hurts presentation delay

When HTML is streamed from the server, the browser parses it in chunks and yields between them. When a script builds large amounts of HTML on the client, the browser does not yield until that HTML is parsed and rendered, which can delay the next frame. On WordPress this shows up in AJAX filters and “load more” buttons that inject a big HTML string at once. Inserting smaller batches, with a yield between them, keeps the frame after the click short.


#What causes poor INP in WordPress

These are the culprits we meet most often when a WordPress site fails INP. None of them is unique to WordPress, but plugins make them easy to install without noticing.

#Heavy page builders

Divi and Elementor often add deep DOM nesting, with several wrapper elements around each widget, which makes layout recalculation after an interaction slower. Check the depth in the Elements panel of DevTools before and after replacing a section with native blocks.

#Chat widgets

Most live-chat scripts are large main-thread consumers that load and start their work on every page. A click-to-load pattern, where a lightweight button loads the real widget only when the user asks for help, removes that work from pages where nobody chats.

#Auto-rotating sliders

Transitions that fire every few seconds can coincide with user clicks. If the browser is in the middle of a slider transition when the user taps a menu link, the interaction has to wait for that work, which shows up as input delay.

#WooCommerce scripts outside the shop

Cart fragments and other store scripts can end up loaded on pages without any shop features, such as blog posts. Check which WooCommerce scripts each template enqueues, and see our WooCommerce performance guide for store-specific fixes.

#Embedded Google Maps

A Google Maps embed loads a significant amount of JavaScript and keeps event listeners active. A static image that loads the interactive map on click avoids that cost for visitors who never use the map.


#Best tools to measure INP in 2026

To fix INP, you need data from real users and a way to reproduce the slow interaction in the lab.

#CrUX API and PageSpeed Insights

The CrUX API documentation describes the data as a 28-day rolling average, updated daily around 04:00 UTC and about two days behind the current date. Each metric comes with a histogram and a p75 value, and you can filter by PHONE, DESKTOP or TABLET. Only opted-in Chrome users on desktop and Android contribute, so Chrome on iOS and other Chromium browsers such as Edge are not in the data. Low-traffic pages often have no page-level record and fall back to origin data. PageSpeed Insights and the Core Web Vitals report in Search Console show the same field data.

#How to find the slow script with Long Animation Frames (LoAF)

The Long Animation Frames API shipped in Chrome 123. It reports any frame whose rendering update is delayed beyond 50 ms and, unlike the Long Tasks API, groups the work by frame, which matches how INP is experienced. Long Tasks is not going away: Chrome says there are no plans to deprecate it, but LoAF gives far better attribution. Each entry carries duration, blockingDuration, renderStart, styleAndLayoutStart and a scripts array. Scripts longer than 5 ms get attribution with sourceURL, sourceFunctionName, invokerType and forcedStyleAndLayoutDuration, which is often enough to name the plugin responsible.

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('LoAF', entry.duration, entry.blockingDuration,
      entry.scripts.map((s) => ({
        sourceURL: s.sourceURL,
        sourceFunctionName: s.sourceFunctionName,
        duration: s.duration,
        forcedLayout: s.forcedStyleAndLayoutDuration,
      })));
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

LoAF is supported in Chrome and Edge. Firefox and Safari do not expose it, so attribution data from field monitoring will only come from Chromium browsers.

#Field data with the web-vitals attribution build

The web-vitals library has an attribution build that tells you why an interaction was slow. The web.dev guide to finding slow interactions in the field lists the fields: interactionTarget (a CSS selector for the element), interactionType, inputDelay, processingDuration, presentationDelay and, since version 4, longAnimationFrameEntries. Send them to your analytics endpoint with sendBeacon:

import { onINP } from 'web-vitals/attribution';

onINP(({ value, rating, attribution }) => {
  navigator.sendBeacon('/analytics', JSON.stringify({
    value,
    rating,
    target: attribution.interactionTarget,
    inputDelay: attribution.inputDelay,
    processingDuration: attribution.processingDuration,
    presentationDelay: attribution.presentationDelay,
  }));
});

The phase breakdown tells you where to look: a large input delay points at work running before the click, a large processing duration at your handlers, and a large presentation delay at DOM size and rendering.

#Web Vitals extension and DevTools

The Web Vitals extension and the Performance panel in Chrome DevTools show the INP of each interaction as you perform it. Click each button, link and form field on your key templates and watch the values. Lab tests on a fast laptop understate mobile latency, so use CPU throttling in DevTools when you reproduce a slow interaction reported from the field. Mobile-specific constraints are covered in our guide to mobile-first performance strategies.


#How to improve INP in WordPress step by step

#Step 1: baseline audit

Pull CrUX data for your origin and your main templates (home, blog post, product, category) and note INP separately for phones and desktops. Start with the templates that have the worst p75 value and enough traffic to matter.

#Step 2: find the slow interactions

Add the web-vitals attribution build to the site, or use the LoAF observer above, and collect the interactionTarget and phase breakdown for slow interactions. This turns “the site feels slow” into a list of specific elements.

#Step 3: script inventory

List all scripts loaded on each template. For each one, note whether it is needed on that template, whether it can use the defer strategy, whether it can load on interaction, and whether it can move to a worker or be removed.

#Step 4: split long tasks

For the scripts LoAF attributes slow frames to, split long tasks with scheduler.yield() and the setTimeout() fallback, starting with the work that runs during or right after an interaction.

#Step 5: reduce DOM and rendering cost

Flatten the deepest sections, add content-visibility: auto to long below-the-fold areas, and consider replacing page builder sections with native Gutenberg blocks where the DOM is deepest.

#Step 6: continuous monitoring

Keep the field monitoring running after the fixes. CrUX needs 28 days to fully reflect a change, so compare your own field data week by week and set an alert when p75 INP crosses 200 ms.


#INP optimization services from WPPoland

At WPPoland, we specialize in the “human-first” performance metric.

  1. JavaScript refactoring: we rewrite bloated plugin code into lightweight alternatives that keep the functionality without the weight.
  2. LoAF diagnostics: we use field telemetry with LoAF attribution to pinpoint the script and function causing your lag.
  3. Interactivity design: we make sure the visible response to a click happens first, and everything else waits until after the paint.

#Conclusion

Core Web Vitals are about how a user feels when interacting with your brand. Interaction to Next Paint is the metric of trust. When your site responds instantly, your users feel in control. When it lags, it feels broken, and on mobile a menu that does not react is often treated as broken. No published study ties an INP band to a fixed bounce or conversion percentage, so measure your own before and after.

Is your WordPress site feeling sluggish? Contact WPPoland to diagnose your INP and fix the interactions that hold it back.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If you are planning a Headless WordPress setup, frontend decoupling, or migration to Astro, I can design and build the architecture, API, and frontend.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Article FAQ

Frequently asked questions

Practical answers to apply the topic in real execution.

SEO-readyGEO-readyAEO-ready3 Q&A
Why did Google replace FID with INP?#
FID only measured the delay of the *first* interaction. INP measures the responsiveness of *all* interactions throughout the entire page lifecycle, making it a much more accurate representation of real-world UX.
How do I measure INP accurately?#
In 2026, we use the Chrome UX Report (CrUX) for field data and the 'Performance' tab in DevTools (with the Long Animation Frames API enabled) for lab testing.
Can a slow server cause poor INP?#
Indirectly. While INP is mostly a client-side (browser) metric, a slow server that delays critical JS execution can lead to a congested main thread when the user finally tries to interact.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles