Skip to content
  • FG-35
  • 12 min read
  • Last checked November 7, 2026

How to Fix Poor INP (Interaction to Next Paint) When Clicks Lag on WordPress

Search Console shows INP in red and buttons feel sticky. Learn what INP measures, find the long tasks behind it in Chrome devtools, and fix the scripts causing them.

Run the health check

Search Console emails you about “INP issue: longer than 200ms”, or PageSpeed Insights shows Interaction to Next Paint in orange or red. In real life it feels like this: you tap the menu button and nothing happens for half a second, you click “Add to cart” and the button seems frozen, or typing into a search box stutters.

What is happening is that the browser’s main thread is busy. The browser can only run one piece of JavaScript at a time on that thread, and it also uses it to draw the screen. If a script is in the middle of a long job when you click, your click waits in line, then your click’s own handlers run, and only after all that can the browser paint the result. INP measures that whole wait. Site owners usually see it after adding marketing tags, a chat widget or a heavy theme, and it often fails on mobile while desktop passes.

What is happening

INP looks at the clicks, taps and key presses a visitor makes during their whole visit and reports one of the slowest (on pages with many interactions, a few extreme outliers are ignored). Scrolling and hovering do not count. Each interaction has three phases:

Phase What it is Usual cause when it is long
Input delay Time from the tap until event handlers can start Other scripts already running a long task: tag managers, page load scripts, timers
Processing duration Time the event handlers themselves take Heavy click handlers, many listeners on the same element, analytics fired synchronously
Presentation delay Time from handlers finishing to the next frame painted Large DOM, expensive style and layout recalculation, big re-renders

The thresholds, judged at the 75th percentile of real visits:

INP Rating
200 ms or less Good
Over 200 ms up to 500 ms Needs improvement
Over 500 ms Poor

A long task is any single chunk of main-thread work longer than 50 ms. The browser cannot respond to input during it. One 400 ms task from a script that happens to run when the user taps gives you a 400 ms input delay before your own code even starts.

INP replaced First Input Delay as a Core Web Vital in March 2024. It is stricter, because it covers every interaction and all three phases, not just the delay before the first one. Loading speed (LCP), image weight and font choices are covered in the design-side speed guide (FG-11); this guide is about responsiveness after the page has loaded.

Before you start

  • Access: WordPress admin, access to your tag manager account if you use one, and ideally a staging copy.
  • Tools: desktop Chrome or Edge for devtools. Open them with F12, or Cmd+Option+I on a Mac.
  • Where commands run: snippets marked “browser console” go in devtools, Console tab. PHP snippets go in a small plugin file in wp-content/mu-plugins/ or your child theme’s functions.php.
  • Backup: take one before changing optimization plugin settings. Delay and combine features can break menus, forms and checkouts.

Step 1: Read the field data correctly

INP is a field metric: it comes from real Chrome users, collected in the Chrome UX Report (CrUX). Lab tools cannot fully reproduce it.

  • PageSpeed Insights: the top section, “Discover what your real users are experiencing”, shows INP from the last 28 days. Check the toggle between “This URL” and “Origin”. If there is not enough data for the URL, it falls back to the whole origin, which means the problem may be on a different template.
  • Search Console: Experience, Core Web Vitals, then the Mobile or Desktop report. Failing URLs are grouped by similar pages, so one fix often clears a whole group. Click an issue to see example URLs. After deploying a fix, use “Validate fix”; Search Console then watches the next 28 days of data.
  • The Lighthouse lab score in PageSpeed Insights does not include INP, because no one clicks during the test. Total Blocking Time is its closest stand-in: a high TBT usually means long tasks that will also hurt INP.

Important: because field data is a 28-day rolling window, fixes take weeks to show up. Use the lab methods below to confirm improvement the same day.

Step 2: Reproduce the slow interaction in devtools

  1. Open the failing page in a Chrome window with no extensions (a guest profile works well), and open devtools.
  2. Go to the Performance panel. Set CPU throttling to 4x or 6x slowdown (in the panel’s environment or capture settings; the exact place varies by Chrome version). Your laptop is much faster than the average phone; throttling brings it closer to what real visitors experience.
  3. The panel’s landing view shows live local metrics, including INP, and an interactions log. Click the things real users click: the menu toggle, accordions, tabs, “Add to cart”, filter checkboxes, the consent banner’s accept button.
  4. Each interaction appears in the log with its duration and the element. Anything over 200 ms is a candidate. Expand an entry to see its input delay, processing duration and presentation delay.

Reading it: if input delay dominates, something else was running when you clicked (Step 3). If processing dominates, the element’s own handlers are slow (Step 5). If presentation delay dominates, rendering the result is expensive (the Advanced section).

Step 3: Find long tasks and attribute them to scripts

Now record a trace. Click the record button, perform the slow interaction once or twice, then stop.

  • Interactions track: each interaction is a bar. Long ones are flagged. Hover to see the three phases.
  • Main track: a flame chart of everything on the main thread. Long tasks have red triangles and red striping in the corner. Look directly under your interaction for tasks that overlap it.
  • Click a long task and open the Bottom-up tab beneath. Switch grouping to “Group by domain” or “Group by URL”. This tells you which script files consumed the time. Your own domain points at theme or plugin scripts; other domains point at third parties.
  • Summary tab: breaks time into Scripting, Rendering and Painting. High Rendering points at layout and style work, not JavaScript logic.
  • Insights sidebar: newer Chrome versions show insights including an INP breakdown and a third parties summary that totals main-thread time per provider.

Script URLs map to WordPress plugins easily: a path such as /wp-content/plugins/PLUGIN_SLUG/assets/js/... names the plugin folder. Third-party domains such as googletagmanager.com name the service.

Step 4: Third-party scripts

On most WordPress sites with failing INP, the biggest long tasks belong to scripts you did not write. The common offenders, and what to do about each:

Tag managers

A tag manager container is a small script that loads many others. Each tag, and every click trigger that listens to all clicks, adds work. In your tag manager, list tags by when they fire. Remove tags for tools no one uses. Prefer tags that fire on page load once rather than on every click. Avoid custom HTML tags that inject more scripts on interaction. Test by temporarily blocking the container: in devtools, Network panel, right-click the container request, choose “Block request URL”, reload and repeat your interaction test. If INP drops from 450 ms to 120 ms, you have found where to work.

Chat widgets

Live chat scripts are often large and run work on load and on every route change. Use a facade: a lightweight button that looks like the chat launcher and loads the real widget only when clicked. Several chat vendors offer a delayed or lazy load option; otherwise many optimization plugins can delay the script until first interaction (see Step 6 for the trade-off).

Heatmaps and session recording

These tools watch every change to the page so they can replay it, which costs main-thread time during exactly the interactions you care about. Run them only on the pages and for the period you are studying, or sample a fraction of visitors in the tool’s settings.

Consent banners

The accept button on a cookie banner is often the single worst interaction on a site, because clicking it releases every blocked tag at once and the banner’s own handler waits for them. Look in your consent tool for options to load tags asynchronously after consent, and check that it does not re-scan the whole page on click.

Step 5: Your own event handlers

If processing duration is the big phase, the element’s handlers are doing too much. To see what is attached, select the element in the Elements panel and open the Event Listeners tab. You will often find several plugins listening to the same click on document.

The fix is to do the visible part first and defer the rest. A handler should update what the user sees, let the browser paint, and only then do analytics, network calls or heavy computation. The standard pattern, in your theme or plugin JavaScript:

function yieldToMain() {
  if (globalThis.scheduler && typeof scheduler.yield === 'function') {
    return scheduler.yield();
  }
  return new Promise(resolve => setTimeout(resolve, 0));
}
const button = document.querySelector('BUTTON_SELECTOR');
const menu = document.querySelector('MENU_SELECTOR');
button.addEventListener('click', async () => {
  menu.classList.toggle('is-open');          // visible change first
  await yieldToMain();                       // let the browser paint
  window.dataLayer = window.dataLayer || []; // non-urgent work after
  window.dataLayer.push({ event: 'EVENT_NAME' });
});

Placeholders: BUTTON_SELECTOR and MENU_SELECTOR are CSS selectors for your toggle button and menu (right-click the element, Inspect, and read its class or id). EVENT_NAME is whatever your analytics expects. What it does: scheduler.yield() is supported in recent Chromium browsers and hands control back to the browser while keeping your work near the front of the queue; elsewhere the setTimeout fallback does a similar job. Good result: in a new trace the interaction’s processing time shrinks to a few milliseconds and the analytics work appears as a separate task after the paint.

Other handler fixes: debounce input handlers on search boxes so they do not run on every keystroke; avoid reading layout values (offsetHeight, getBoundingClientRect()) straight after changing styles in a loop, which forces repeated layout; and replace dozens of per-item listeners with one delegated listener.

Step 6: Delay, defer and async in WordPress

Method When it downloads When it runs Best for
Normal script Blocks parsing Immediately Almost nothing
async In parallel As soon as it arrives, in any order Independent scripts such as analytics
defer In parallel After the HTML is parsed, in order Theme and plugin scripts with dependencies
Delay until interaction Not until first scroll, tap or key press On that first interaction, or after a timeout Chat, heatmaps, social embeds

Since WordPress 6.3 you can register scripts with a loading strategy in PHP:

wp_register_script( 'SCRIPT_HANDLE', get_stylesheet_directory_uri() . '/js/FILE_NAME.js', array(), '1.0', array(
    'strategy'  => 'defer',
    'in_footer' => true,
) );

Placeholders: SCRIPT_HANDLE is a unique name you choose, FILE_NAME your script file in the child theme’s js folder. WordPress automatically falls back to normal loading if a dependency requires it.

Optimization plugins such as WP Rocket, Perfmatters, LiteSpeed Cache and Flying Scripts offer a “delay JavaScript” feature. Use it with care: delayed scripts all execute on the visitor’s first interaction, so that first tap can become the slowest interaction of the visit and make INP worse. Delay only scripts that are not needed for the first click (chat, heatmaps, social widgets), and exclude menus, sliders, forms, cart and consent scripts. Test the very first click after a fresh load with throttling on.

Step 7: Measure real-user INP with attribution

Devtools tells you what happens on your machine. The web-vitals library’s attribution build tells you what happens to real visitors. To try it on one page view, paste this into the browser console:

const s = document.createElement('script');
s.type = 'module';
s.textContent = `
import {onINP} from 'https://unpkg.com/web-vitals@5/dist/web-vitals.attribution.js?module';
onINP(({value, rating, attribution}) => {
  console.log('INP', Math.round(value), rating, attribution.interactionTarget);
  console.log('input', Math.round(attribution.inputDelay),
    'processing', Math.round(attribution.processingDuration),
    'presentation', Math.round(attribution.presentationDelay));
  if (attribution.longestScript) {
    console.log('longest script', attribution.longestScript.entry.sourceURL,
      attribution.longestScript.subpart);
  }
}, {reportAllChanges: true});`;
document.head.appendChild(s);

Where: browser console, on your page. Then click around. reportAllChanges: true logs every time the page’s INP worsens instead of waiting until you leave. Reading the output: interactionTarget is a CSS selector for the element clicked; the three numbers are the phases in milliseconds; longestScript names the script file that ran longest during the interaction and which phase it fell in. If your site has a strict Content Security Policy the import may be blocked; that is expected.

For ongoing monitoring, a developer can bundle the library and send these values to your analytics as events, so you can see which elements and scripts cause slow interactions across thousands of visits, not just yours.

Advanced: Presentation delay and DOM size

When the handler is quick but the paint is late, the browser is spending time on style recalculation and layout. In the trace, purple “Recalculate style” and “Layout” blocks after your handler confirm it.

  • Large DOM: page builders nest many wrapper elements per section. Mega menus that render every item on every page, hidden off-canvas copies of the menu, and long product grids multiply the count. Run document.querySelectorAll('*').length in the browser console to see the element count. Lighthouse flags an excessive DOM size; simplifying sections and removing duplicated mobile and desktop markup helps directly.
  • Off-screen content: the CSS property content-visibility: auto on long below-the-fold sections lets the browser skip their layout until needed. Pair it with contain-intrinsic-size so the scrollbar does not jump.
  • Toggling a class on body or html can force style recalculation for the entire page. Toggle classes on the smallest container that needs to change.
  • WooCommerce: AJAX add-to-cart updates fragments of the page; if many fragments re-render, each click is expensive. Check what your theme registers and keep the mini cart simple.

For script-heavy tasks you cannot remove, the Long Animation Frames data in your trace (and in the web-vitals attribution as longAnimationFrameEntries) shows exactly which script, function and invoker ran in each slow frame, which is the evidence a plugin developer needs to fix their code.

When to contact your host

INP is almost entirely a browser-side problem, so your host can rarely fix it directly. Contact them if your optimization plugin’s settings are managed at the server level, or if you need a staging site to test script changes. Contact the plugin or service vendor instead when attribution points to their script; send the page URL, the slow interaction, the throttling used, a saved trace file (Performance panel, download icon), and the longestScript URL. Note the date and UTC time of your tests.

For quick outside checks, see our diagnostic tools. If the page is not just laggy but frozen or crashing the browser, start at the Emergency Room.

Still stuck?

Print a free one-page checklist to keep, or open the Emergency Room if the symptom does not match.

Get the free checklists

Keep reading

All field guides
FG-130 Performance

How to Fix Cumulative Layout Shift (CLS) on WordPress

Content jumps as the page loads and Search Console reports a CLS issue. Find the element that moves and why, then reserve space for images, embeds, ads, fonts and banners.

Read the guide
10 min read

Search the field manual

What is going wrong?

Press / to search from any page, Esc to close