Skip to content
WP speed & Core Web Vitals

How to Fix INP on WordPress: 8 Common Culprits and Their Cures

WordPress INP failing Core Web Vitals? Eight common named culprits, named-plugin fixes, and the diagnostic chain we run at WitsCode before touching code.

By WitsCode9 min read
WP speed & Core Web Vitals

Interaction to Next Paint became a stable Core Web Vital in March 2024, replacing First Input Delay, and it punishes WordPress sites in a way FID never did. Where FID measured only the queue time before a handler started, INP measures the entire round trip from a user's tap or click to the next visual update on screen. Anything above 200 milliseconds at the 75th percentile counts as needs improvement, and anything past 500 milliseconds is officially poor. The fastest single fix on most WordPress sites is to delay all non-essential JavaScript execution until first user interaction using WP Rocket's Delay JS feature or Perfmatters' equivalent. That one toggle typically strips 150 to 250 milliseconds off INP because chat widgets, analytics tags, and affiliate scripts stop competing with the click handler the user actually wanted to run.

The second fastest fix, specific to WooCommerce, is to disable cart fragments on every page that is not the cart or checkout. WooCommerce ships with an admin-ajax-powered mini-cart refresh script called wc-cart-fragments that polls and re-renders on virtually every interaction, even on a blog post that has nothing to do with the cart. Drop it with the Disable Cart Fragments plugin or a one-line filter, and INP on category and blog pages often falls under the 200 millisecond threshold without any other change. Those two fixes alone resolve perhaps half the WordPress INP failures we audit at WitsCode. The remaining six require diagnosis. The rest of this article is the field guide we use.

What INP actually measures on a WordPress page

INP is the slowest interaction the browser observes during a page's lifetime, with one or two outliers discarded on long sessions. It is composed of three distinct phases that any fix has to address in the right order. The input delay is the time the browser waits before the event handler can start running, usually because the main thread is already busy with another task. The processing time is how long the registered handlers actually take to execute, which on WordPress is dominated by jQuery, plugin click bindings, and any framework-level reactivity. The presentation delay is the time after handlers finish before the browser paints the resulting state, which is hurt by large layout invalidations and synchronous style recalculations. A fix that targets only one phase rarely moves the metric. A fix that targets the wrong phase moves nothing at all.

Diagnosing which phase dominates requires Chrome DevTools' Performance Insights panel, the Long Animation Frames API exposed through window.performance in Chrome 123 and later, or a real-user monitoring script using the web-vitals.js library to log live INP values from the field. PageSpeed Insights gives you the field data summary but not the offending script. Lab tests in Lighthouse will not catch INP at all unless you simulate interactions, which most WordPress owners never do. We assume nothing about which culprit is at fault until the trace tells us. The eight that follow are the ones the trace almost always names.

The eight culprits and their named-plugin cures

The first repeat offender is WooCommerce's cart fragments script, which we mentioned in the opening. The script lives at /wp-content/plugins/woocommerce/assets/js/frontend/cart-fragments.js and exists to keep a mini-cart UI synchronized with server-side cart state. The implementation calls admin-ajax.php on a debounced interval and on every cart change, blocking the main thread while the response is parsed and the cart markup is replaced. On non-cart pages this work is pure waste. The named fix is the Disable Cart Fragments plugin by Optimocha for non-developers, or the filter wp_dequeue_script gated by a not-is-cart-or-checkout condition for anyone willing to drop ten lines into a child theme. Sites running CartFlows, FunnelKit, or a custom drawer cart should also audit whether their drawer fetches fragments on hover or only on click, because hover-triggered fetches are an INP killer the team often forgets about.

The second culprit is the third-party chat widget. Intercom, Drift, tawk.to, Tidio, HubSpot Chat, and Crisp each ship between 200 and 400 kilobytes of compressed JavaScript that initializes on page load and binds global event listeners for tracking dwell time, scroll depth, and click intent. Those bindings sit in the input delay phase and steal cycles from the actual click the user is making. The cure is never to remove the chat tool but to gate its loading behind genuine user intent. WP Rocket's Delay JS, Perfmatters' Delay JS, and FlyingPress's defer execution all support a "load on first interaction" mode that postpones the chat bundle until after the first scroll, mouseover, or tap. A more aggressive variant replaces the live widget with a static button placeholder that loads the real script only when the button is clicked, which we implement as a six-line vanilla JavaScript snippet on most builds.

The third culprit is the page builder runtime, and Elementor and Divi are the usual names. Elementor's frontend.js, swiper.js, and share-link.js bundle adds roughly 200 kilobytes of execution to every page even when the page uses no carousels or share links. Divi's monolithic bundle exceeds 600 kilobytes uncompressed and runs reactivity polling on every interaction. Fixes range from surgical to nuclear. Asset CleanUp Pro by Gabe Livan lets you dequeue per-page any builder asset the page does not use. Elementor's experimental flags labelled "Optimized DOM Output" and "Improved Asset Loading" remove a meaningful slice of unused CSS and JS once enabled in Settings, Experiments. The nuclear option, which we recommend for any site rebuilt from scratch, is to switch to Bricks Builder or GenerateBlocks Pro, both of which ship near-zero runtime JavaScript and produce INP scores under 100 milliseconds without further tuning.

The fourth culprit is delegated jQuery handlers stacked across plugins. WordPress's culture of binding $(document).on('click', selector) handlers means a single click on a button bubbles through every handler attached to the document by every plugin on the page. We have audited sites with 40 such handlers, each running its own selector match and conditional. The fix begins with dequeuing jQuery Migrate via wp_deregister_script in functions.php or via the Performance Lab plugin's modules, which removes a layer of legacy shims most modern themes do not need. The deeper fix is auditing each plugin's contribution with the Performance panel's Event Listeners breakdown and replacing delegated handlers with scoped ones that bind to the actual element rather than the document. Plugins that consistently show up here include Yoast SEO's admin bar bindings on the frontend and any social plugin still using jQuery 1.x patterns.

The fifth culprit is the form validation library, and the worst offenders are the conditional logic engines inside Gravity Forms, WPForms Pro, and Ninja Forms. Conditional logic re-evaluates on every keystroke, every blur, and every change event, which is the entire purpose of the feature, but the cost is a long task on each keypress that pushes INP into the red whenever the user is typing in a long field. Fluent Forms by WPManageNinja runs a lighter validation layer and produces noticeably lower INP on like-for-like forms. For builds where switching plugins is off the table, Flying Scripts by Gijo Varghese will defer the form's JavaScript until the form scrolls into view, which removes the cost on pages where the form sits below the fold. HTML5 native validation, set via the required and pattern attributes, replaces a meaningful slice of plugin validation work for free.

The sixth culprit is the gallery or lightbox plugin. FooGallery, Envira Gallery, NextGen Gallery, and the older Lightbox 2, Magnific Popup, and Fancybox libraries all bind click handlers globally and parse the entire image DOM on every page load to figure out which images should open in a modal. The lighter alternative is GLightbox at roughly 12 kilobytes, which lazy-binds and only parses the gallery container it is told about. Spotlight by RebelCode is the modern WordPress-native option and uses the Intersection Observer API to defer binding until the gallery is actually visible. Sites that use only the WordPress core block gallery can get away with the native HTML dialog element and a 30-line script, which is what we ship by default on WitsCode builds where photography is not the central feature.

The seventh culprit is the social share plugin. AddThis, ShareThis, Monarch, and Sassy Social Share inject between 100 and 300 kilobytes of JavaScript whose primary job is to fetch share counts from third-party APIs and bind click handlers that open share popups. The share count fetches alone are a dependable INP killer because they fire on idle, refresh on viewport change, and contend for the main thread when the user clicks. The cure is to drop the plugin entirely and replace it with static anchor tags pointing at the share URLs each platform documents. Twitter, Facebook, LinkedIn, and email each accept a query-string share link that requires zero JavaScript. Where a more polished implementation is needed, Grow Social by Mediavine or Simple Share Buttons Adder configured in no-script mode produces equivalent UX without the runtime cost, and the navigator.share Web Share API handles mobile sharing natively in two lines.

The eighth culprit is the advertising or affiliate script. Google Publisher Tag, Ezoic, Mediavine, AdThrive, and Amazon's OneLink all run continuous viewport observers that refresh ad slots and bind scroll handlers that recompute on every interaction. INP suffers because the user's click triggers a viewport recalculation that the ad code has chosen to do synchronously. The mitigation is rarely to remove the ads, since they are the revenue model, but to relocate their loading. Cloudflare Zaraz and a server-side Google Tag Manager container both move the bulk of analytics and ad initialization off the browser main thread. For ads that must run client-side, deferring the init call to requestIdleCallback and giving below-the-fold ad iframes the loading="lazy" attribute removes the largest spikes. Network-level publishers like Mediavine and Raptive will configure a "lazy load below the fold" toggle on request, and that toggle exists for this exact reason.

The diagnostic chain we run before touching anything

The temptation on a failing INP score is to start dequeuing scripts immediately, and that temptation produces broken sites. Our chain begins with PageSpeed Insights for the field data, specifically the section labelled "Discover what your real users are experiencing", because lab data does not include INP. We confirm the failure is real and not a single outlier. We then load the page in Chrome with the Performance panel recording, click the buttons users actually click, and read the Interactions track to find the worst offender. We cross-reference the script attribution with the Long Animation Frames entries for the slowest interaction. Only then do we open the WordPress admin and form a hypothesis about which plugin owns the offending script. The hypothesis is verified in a staging environment by dequeuing the suspect script and re-recording. If INP recovers, the diagnosis was correct, and the fix moves to production with the named-plugin remediation listed above. If it does not recover, we go back to the trace.

What an INP audit at WitsCode actually changes

A typical WitsCode INP audit touches three to six of the eight culprits, leaves the others alone, and ships a written change log explaining which plugin was reconfigured, replaced, or left intact. The deliverable includes before-and-after CrUX field data after a 28-day collection window, because INP is a real-user metric and lab numbers prove nothing. The price is fixed, the scope is bounded by the eight culprits, and the warranty is that 75th percentile CrUX INP lands under 200 milliseconds within two cycles or we keep working until it does. If you have a WordPress site flagged for poor INP in Search Console and would rather not run the chain yourself, that is what we are here for. The fastest path from a failing score to a green one starts with delaying JavaScript and disabling cart fragments, but the durable fix is the audit that finds whichever culprit is specific to your stack.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

WordPress Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss wp speed & core web vitals for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.