How to fix Interaction to Next Paint (INP) on a WordPress site
INP is how long a page takes to show a response to a tap, a click or a key press, reported close to the worst one of a visit. A page that loads fast fails it when JavaScript keeps the browser busy. Find the slow interaction, then remove, delay or split the script behind it.
- By
- WP Ministry
- Published
In short
- A good INP is 200 milliseconds or less for three visits in four, judged separately on phones and on desktops.
- A speed test never taps anything, so it cannot measure INP. Go by the field figure, and treat Total Blocking Time as a hint.
- Find the slow interaction before changing anything. Chrome's Performance panel splits it into input delay, processing duration and presentation delay.
- On WordPress the JavaScript comes from plugins, the theme, a page builder and other companies' scripts. Loading less of it is the fix that lasts.
- Deferring a script changes when it runs, not how much work it does.
- Field data covers the previous 28 days, so a fix takes about four weeks to show in full.
Interaction to Next Paint (INP) measures how long a page takes to show a response after a visitor taps, clicks or presses a key. It watches every such interaction of the visit and reports close to the worst one. A page can load fast and still fail it, because the cause is JavaScript keeping the browser busy at the moment the visitor acts. On a WordPress site that JavaScript mostly comes from plugins, the theme, a page builder, and other companies' scripts such as chat widgets, analytics and adverts.
So an INP issue in Search Console is not about loading. This page shows how to find the interaction that is slow, and what to change once you have. For all three metrics side by side, see how to improve WordPress Core Web Vitals.
What INP counts
- Clicks, taps and key presses. Scrolling and hovering are not counted.
- The time until the next frame is painted. The clock starts at the tap and stops when the browser next draws the screen. A spinner that appears is a response. A request to the server that follows is not part of the figure.
- The slowest interaction of the visit. On a visit with a great many interactions, one of the worst is set aside for every 50.
- Framed widgets too. A tap inside an embedded video player or another iframe on your page counts toward your page in Google's data.
INP replaced First Input Delay as a Core Web Vital on March 12, 2024. First Input Delay timed only the wait before the first interaction.
Every interaction has three parts, and the long one tells you where to look.
| Part | What happens in it | What makes it long |
|---|---|---|
| Input delay | The tap waits for the page's code to start answering it | The browser is busy with something else: scripts still starting up, a timer, an earlier interaction |
| Processing duration | The code that answers the tap runs | Event handlers that do too much before the screen can change |
| Presentation delay | The browser works out the new layout and paints it | A very large page, or a lot of the page changing at once |
A page's JavaScript and its layout work share one main thread, which does one task at a time. A task of more than 50 milliseconds is a long task, and a tap that arrives during one waits for it.
Why a speed test does not show it
A lab test loads the page and stops. Nothing is tapped, so there is no interaction to time. PageSpeed Insights therefore shows INP in only one of its two halves.
- Field data, at the top, comes from the Chrome User Experience Report: real Chrome users over the previous 28 days. INP is there when the page has enough visits. With too few, the report falls back to the whole site, and then to nothing.
- The lab test below it is run by Lighthouse and has no INP. Its stand-in is Total Blocking Time, which Lighthouse describes as the time "a page is blocked from responding to user input": the part of every long task beyond 50 milliseconds, added up while the page loads.
Treat Total Blocking Time as a hint. web.dev says it correlates well with INP and is not a substitute. It covers loading only, so a page can score well on it and still have a menu that is slow a minute later.
The figures that count are in Search Console's Core Web Vitals report, which draws on the same Chrome data. It groups similar pages and gives each group one figure: three visits in four had this INP or better over the last 28 days. Only indexed pages with enough data appear.
Find the slow interaction
Step 1: See which pages and which device
In Search Console, open the Core Web Vitals report, then the report for Mobile or Desktop. Under "Why URLs aren't considered good", select the INP issue to see example addresses. Each example stands for a group of similar pages.
Read the pattern. Every kind of page failing points at something that loads everywhere: the header menu, a consent banner, a chat widget. One kind of page failing points at that template. When phones fail and desktops pass, the same scripts are taking longer on slower processors.
Step 2: Use the page in Chrome with the Performance panel open
Open the page in Chrome, then the developer tools, then the Performance panel. It opens on a live view that shows INP as soon as you interact, with a list of interactions beneath that gives the element and the phases of each.
Your computer is faster than most phones. Under Environment settings, set CPU to a slowdown. Chrome's documentation says throttling cannot truly imitate a phone's processor, so an Android phone connected by remote debugging is closer to the truth.
Now behave like a visitor. Open the menu, type in the search box, add a product to the cart, and accept the cookie banner in a private window. Do some of it while the page is still loading, when the browser is busiest.
Older guides point to the Web Vitals extension. Chrome ended support for it on January 7, 2025 and moved its features into this panel.
Step 3: Record the slow interaction
Press Record, perform the slow interaction, and stop. In the recording, the Interactions track marks anything over 200 milliseconds with a red triangle. Hover over it for the three parts, which the tooltip words as input delay, processing time and presentation delay. The panel's INP breakdown insight calls the middle one processing duration.
Select "Dim 3rd parties" to gray out other companies' scripts. With a range selected and no event selected, the Summary tab's "1st / 3rd party" table gives each company's time on the main thread.
Step 4: Ask the browser which element and which script
The
web-vitalslibrary from Google's Chrome team reports the same breakdown as data. Paste this into the Console tab, then use the page. It fetches the library into your own tab and prints to your console. Nothing on the site changes.javascriptconst { onINP } = await import('https://unpkg.com/web-vitals@5/dist/web-vitals.attribution.js?module'); onINP(({ value, rating, attribution }) => { console.log('INP', value, rating, { element: attribution.interactionTarget, inputDelay: attribution.inputDelay, processingDuration: attribution.processingDuration, presentationDelay: attribution.presentationDelay, script: attribution.longestScript?.entry.sourceURL, }); }, { reportAllChanges: true });
A new line prints whenever a slower interaction raises the figure. element is a CSS selector for what was tapped, such as button#save. script is the address of the script that ran longest during the interaction, and is empty when the browser could not name one. One under /wp-content/plugins/ belongs to the plugin in that folder, one under /wp-content/themes/ to the theme, and one on another domain to a service the site loads. To collect this from real visitors, a developer adds the library to the site itself and sends the result to your analytics.
Fix it by cause
Make one change at a time on a staging copy of the site, then record the same interaction again.
Too much JavaScript on every page
The browser parses, compiles and runs every script on the main thread. Enough of that during loading produces long tasks, and a visitor who taps early gets a long input delay.
Find what loads. The Network panel's JS filter lists every script file the page asked for, and the Coverage panel shows how much of each the page used. On the WordPress side, Query Monitor's Scripts panel lists the scripts WordPress has queued, with each one's name, its dependencies and dependents, and the plugin or theme that registered it. Its documentation suggests using the panel to spot plugins that load on every page when only some pages need them. By default only administrators see its output.
Remove what you do not use. How to choose a WordPress plugin covers removing one properly. To find which plugin slows an interaction, switch plugins off one at a time on the staging copy, the method in how to find a plugin conflict.
Load a plugin's script only where it is used. Look in the plugin's settings first. Failing that, a few lines of code remove a script from the pages that do not need it. WordPress knows each script by a name called a handle. In the page source, the script tag's id is the handle followed by -js.
add_action( 'wp_enqueue_scripts', 'example_child_limit_form_script', 100 );
function example_child_limit_form_script() {
if ( is_page( array( 'contact', 'request-a-quote' ) ) ) {
return;
}
wp_dequeue_script( 'example-form' );
}wp_enqueue_scripts is the hook where scripts for the front end are queued. wp_dequeue_script() removes a script that is already queued, so the 100 makes this run late, after the plugin's own code. It goes in the functions.php of a child theme or in a small plugin.
Do not combine the files to compensate. web.dev points out that each script file is evaluated in a task of its own, so one large combined file becomes one long task. See how to minify CSS and JavaScript in WordPress.
To confirm: the tag is gone from the source of the other pages, the feature works where it is needed, and a tap early in the load has a shorter input delay.
Scripts from other companies
Chat widgets, tag managers, adverts, embeds and social widgets run on the same main thread as your own code. Some keep working after the page has loaded, on timers that fire again and again, and a timer that fires as the visitor taps adds to the input delay. You cannot change their code. You can decide whether, where and when each one loads.
- Tag managers. web.dev reports a correlation between the size of tag managers and poorer INP. Remove tags nobody uses instead of blocking them with a trigger, because a blocked tag's code stays in the container. Fire tags that are not essential after the Window Loaded event, and keep click and timer triggers few.
- Chat widgets, video players and social buttons. Chrome's documentation recommends a facade for all three: a static element that looks like the real thing and loads it on a click. How to lazy load images and videos shows it for YouTube. A map that visitors only look at can be a picture that links to the real one.
- Adverts. web.dev's advice is to load an advert further down the page only when the visitor scrolls near it.
- Analytics. Delay it too long and you can miss data, which is web.dev's own warning.
Some optimization plugins offer to hold back every script until the visitor's first movement. WP Rocket's documentation for its "Delay JavaScript execution" option says that on the first touch, scroll, key press or mouse movement, the browser executes all the scripts. That moves the work to the moment the visitor starts using the page. It does not remove it. Record the first tap with the setting on and with it off, and keep whichever is faster.
To confirm: the company's time in the "1st / 3rd party" table is lower, and the interaction that suffered has a shorter input delay.
Long tasks in the theme or the page builder
Menus, sliders, accordions, tabs, filters and animations are code that runs when the visitor acts. A handler that does a great deal before the screen changes shows up as processing duration. A slider that advances on a timer, or an animation driven by JavaScript, shows up as input delay on whatever the visitor taps next.
What a site owner can change is the settings. Switch off autoplay on sliders and carousels. Switch off entrance animations and scroll effects you can live without. Replace a slider with one still image.
What needs a developer is the handlers themselves. web.dev's guidance is to do as little as possible in a handler, and to show the response first and then yield to the main thread, so the browser can paint before the rest runs.
function yieldToMain() {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}
const button = document.querySelector('.example-filter-button');
const panel = document.querySelector('.example-filter-panel');
button.addEventListener('click', async () => {
// What the visitor is waiting to see comes first.
panel.hidden = false;
// Let the browser draw that before the rest runs.
await yieldToMain();
fillPanel(panel);
recordClick('filter-opened');
});scheduler.yield() is not in every browser yet, which is why the function falls back to setTimeout.
For interactive blocks there is a layer in WordPress to build on. The Interactivity API, in WordPress since 6.5, gives developers one standard way to add interactions to blocks, and the Navigation, Search, Query and File blocks use it.
To confirm: record the same interaction. The processing duration is lower.
A very large page
web.dev's account of DOM size is short: the more elements a page has, the more it costs to draw and to redraw. When an interaction changes the page, the browser recalculates styles and layout, and on a very large page that work is the presentation delay.
Count the elements by typing document.querySelectorAll('*').length into the console. Lighthouse's DOM size audit warned at about 800 nodes and flagged a page at about 1,400.
Page builders tend to produce deep markup, and one builder's own notes show the mechanism. Elementor's developer notes for version 3.26 describe every widget being wrapped in two div elements, because the wrappers carry features such as backgrounds, motion effects and layout. The same notes describe an "Optimized Markup" experiment that reduces the two to one. Chrome's documentation adds that depth is not a fault by itself. It is a symptom of nesting that is not needed.
What helps: fewer sections and widgets on a page, one container where a section holds a section that holds a column, fewer products or posts in one listing, and your builder's option for leaner markup if it has one and its documentation calls it ready.
To confirm: the element count is lower, and the presentation delay with it.
How scripts are loaded: defer and async
A script tag with defer or async no longer stops the browser reading the page while the file downloads. Since WordPress 6.3, a theme or plugin can ask for either through WordPress's own functions, with a strategy key in the last argument of wp_register_script() or wp_enqueue_script(). Since 6.4, WordPress loads the front-end scripts of blocks with defer.
add_action( 'wp_enqueue_scripts', 'example_child_enqueue_stats' );
function example_child_enqueue_stats() {
wp_enqueue_script(
'example-stats',
'https://stats.example.com/script.js',
array(),
null,
array(
'strategy' => 'async',
'in_footer' => true,
)
);
}async suits a script like this one, which needs nothing else and which nothing else needs. For a script a plugin has already registered, wp_script_add_data( 'example-handle', 'strategy', 'defer' ) sets the strategy on its handle. How to fix render-blocking CSS and JavaScript has that method in full.
This is not an INP fix by itself. WordPress's note on the 6.3 change presents it as a gain for Largest Contentful Paint, the loading metric. A deferred script still runs on the main thread, only later, and web.dev notes that Chrome runs all deferred scripts in one task, which can itself be a long one.
What can break: an async script runs whenever it arrives, in no set order, so code that relies on it may run first and fail. WordPress guards the order for scripts it knows about. It keeps a script blocking if a script that depends on it is blocking, or if inline code has been attached after it.
To confirm: the tag in the page source carries async and data-wp-strategy="async". A tag with the second attribute and without the first is one WordPress kept blocking.
The consent banner
A consent banner is shown to every new visitor, so its button can be the first interaction of a visit, whichever page they land on. web.dev says cookie notices are often a cause of high INP for one reason: accepting sets off a great many third-party scripts at once, and all of that work sits between the tap and the next paint.
Test it the way a new visitor meets it: in a private window, record, and press the button. Fewer tags behind the banner means less work on that tap. Check also whether your consent tool has an update. web.dev reports that the Chrome team has worked with several consent platforms so that they yield after the tap and let the browser paint before the scripts start.
From what the visitor does to the fix
| What the visitor does | What is slow | Likely cause | Fix |
|---|---|---|---|
| Taps the menu button while the page is still loading | Input delay | Scripts are still being parsed and run | Fewer scripts on the page. Other companies' scripts later. |
| Presses the button on the cookie banner | Processing duration | Every consented script starts at once | Fewer tags. A consent tool that yields after the tap. |
| Opens a menu, an accordion or a tab on a page that has finished loading | Processing duration | A heavy handler in the theme or the builder | Switch off effects. A developer splits the handler. |
| Types in a search box that suggests results | Each key press in turn | A handler doing work on every key | A developer makes it wait until the typing pauses. |
| Adds to the cart, or applies a filter | Presentation delay | A large page redrawn, or a lot of HTML inserted | Fewer elements. Fewer items in a listing. |
| Taps anything, and now and then it lags | Input delay | A timer: slider autoplay, a chat widget, adverts | Switch off autoplay. Remove or delay the third-party script. |
| Taps anything on a phone, and even a plain page is slow | Input delay | No mobile viewport, so the browser waits to see whether a tap is a double tap | The theme has to declare a mobile viewport. |
The server side of a store's speed is a separate job, covered in how to speed up a WooCommerce store.
What good looks like, and how long the figures take to move
Good is an INP of 200 milliseconds or less. Over 200 and up to 500 needs improvement, and over 500 is poor. The figure judged is the 75th percentile, so three visits in four must be at or under it, and phones and desktops are judged separately.
After a fix, a recording in Chrome tells you at once whether the interaction is faster. The field figure follows slowly. The Chrome data behind PageSpeed Insights and Search Console covers a trailing 28 days, so the full effect takes about four weeks. In Search Console, select "Start Tracking" on the issue's page. It watches for 28 days and counts the issue as fixed if it does not appear in that time.
Expect to go round more than once. web.dev describes the work as iterative: fix one slow interaction and you are likely to find the next.
If you would rather hand the job over, our speed optimization service is a one-time job: you name the pages, and we measure them before the work and again after it. We do not promise a particular score. For everything else that makes a WordPress site slow, start with how to speed up WordPress.
Common questions
My site loads fast. How can INP fail?
INP is not a loading metric. It times what happens after a tap, a click or a key press, at any point in the visit. A page can arrive quickly and still be running scripts when the visitor reaches for the menu, or run a heavy handler every time a filter is pressed.
PageSpeed Insights shows no INP for my page. Why?
Either the page has too few visits from Chrome users for Google to publish a figure, or its visitors rarely tap or type anything. A page nobody interacts with has no INP. When INP data is missing, the Core Web Vitals assessment is made on the other two metrics.
I cannot make the page slow on my own computer. What now?
Your computer is probably much faster than your visitors' phones. Throttle the CPU in the Performance panel, or test on a real Android phone. Then interact while the page is still loading, on a throttled network if yours is fast, because that is when the main thread is busiest.
Will a caching plugin or a CDN fix INP?
Not by delivering the page faster. A page cache and a CDN shorten the wait for the page and its files, and the same scripts then run in the visitor's browser. What can change INP is a script setting such a plugin offers, like deferring, delaying or leaving out a script, and each of those needs testing.
- Cost guideWordPress speed optimization cost: what the job is, what it should include and what moves the price
- Guideadmin-ajax.php high CPU usage in WordPress: how to find what is calling it
- GuideAI bots slowing down your website: how to confirm it on WordPress and what to do, mildest first
- GuideHow to clean up and optimize a WordPress database, and what it does for speed
- GuideHow to enable gzip and Brotli compression in WordPress
- GuideHow to fix Cumulative Layout Shift (CLS) in WordPress: find what moves and reserve its space

