Core Web Vitals & E-E-A-T

How to Fix High INP Score in WordPress (Interaction to Next Paint)

INP replaced FID in 2024. Most WordPress sites fail it. Here's the diagnosis + the top-3 fixes by impact.

Published May 24, 20267 min readBy RankCrab Team

INP — Interaction to Next Paint — replaced First Input Delay as a Core Web Vitals metric in March 2024. FID was relatively easy to pass because it only measured the delay before the browser started processing the first user interaction. INP measures the full latency of every interaction — every click, tap, and keypress — for the entire page session, and takes the worst one as the score.

Most WordPress sites fail INP. The Good threshold is under 200ms. A site loaded with a popular page builder, a contact form plugin, a chat widget, and an analytics script often sits at 400–700ms.

Here's how to find out exactly what's failing and fix the highest-impact issues first.

What INP Measures (And Why WordPress Struggles)

When a user clicks something, the browser has to:

  1. Finish any currently executing JavaScript (the "long task" that's blocking the main thread)
  2. Process the event handler
  3. Run layout, paint, and composite to show the visual response

INP captures steps 1 through 3. If the main thread is busy with a long task — parsing a 400kb JavaScript bundle, running a third-party script, executing a heavy event handler — the browser can't respond to input until that task finishes.

WordPress's plugin ecosystem is the problem. Every plugin that adds JavaScript to the front end is potentially blocking the main thread. Page builders (Elementor, Divi, Beaver Builder) add large JS bundles. Contact form plugins add event listeners. Analytics and ad scripts run on every interaction. Chatbots are particularly bad — they're large, often poorly optimized, and load eagerly.

Step 1: Diagnose the Worst Interaction

Before fixing anything, identify what's causing the high INP. PageSpeed Insights shows an INP score in the Field Data section (from real Chrome users) and may show a specific interaction in the Diagnostics section. But for actionable detail, use Chrome DevTools.

Open Chrome DevTools, go to the Performance panel, and enable Web Vitals in the toolbar. Reload the page and interact with it normally — click a menu, open a dropdown, submit a form. After recording, look for:

  • Long tasks (red bars above the flame chart) that block during your interactions
  • INP candidates listed in the Web Vitals overlay

The flame chart shows which JavaScript is running during the long task. That's your target.

Fix 1: Defer Non-Critical JavaScript

The highest-leverage fix for most WordPress sites. JavaScript that runs during page load on the main thread extends the window during which interactions will be blocked. Deferring non-critical scripts shrinks that window.

In functions.php, you can add defer to scripts enqueued by plugins:

<?php
add_filter( 'script_loader_tag', function( $tag, $handle, $src ) {
    // List of script handles to defer
    $defer_scripts = [
        'contact-form-7',
        'wpcf7-recaptcha',
        'google-analytics',
    ];

    if ( in_array( $handle, $defer_scripts, true ) ) {
        return str_replace( ' src=', ' defer src=', $tag );
    }
    return $tag;
}, 10, 3 );

Be careful with defer: it still executes after HTML parsing, before DOMContentLoaded. For scripts that don't need to run until user interaction, consider async instead — it executes as soon as it downloads, without blocking parsing or DOMContentLoaded ordering.

For the most aggressive deferral — delaying a script until the user moves their mouse or taps the screen — WP Rocket's "Delay JavaScript Execution" and Perfmatters' "Delay Scripts" do this automatically for common third-party scripts.

Fix 2: Break Up Long Tasks

A long task is any JavaScript that takes more than 50ms to run on the main thread. If your event handlers are doing heavy work synchronously (DOM manipulation, complex calculations, synchronous XHR), they create long tasks that block INP.

The fix is scheduler.yield() — a newer API that lets you break a long task into chunks, yielding control back to the browser between them:

async function handleButtonClick() {
  // Do first chunk of work
  processFirstHalf();

  // Yield to browser — allows it to render and handle other input
  await scheduler.yield();

  // Do second chunk of work
  processSecondHalf();
}

For browsers that don't support scheduler.yield() yet, use setTimeout with 0ms delay as a fallback:

function yieldToMain() {
  return new Promise( resolve => setTimeout( resolve, 0 ) );
}

async function handleButtonClick() {
  processFirstHalf();
  await yieldToMain();
  processSecondHalf();
}

This pattern is most useful when you control the JavaScript. For third-party scripts, you can't break up their tasks — you can only delay when they load.

Fix 3: Audit and Remove Heavy Third-Party Scripts

Third-party scripts are the biggest INP culprit on most WordPress sites. They run on a main thread you share with everything else, they load resources you don't control, and they often add event listeners that fire on every user interaction.

Run a third-party audit in PageSpeed Insights under Diagnostics > Reduce the impact of third-party code. This table shows every third-party script, its total blocking time, and its transfer size.

Common offenders and their alternatives:

ScriptProblemAlternative
Intercom / Drift chatLarge bundle, eager loadDelay until scroll or click
Google Tag ManagerContainer can load dozens of tagsAudit tags, remove unused
Hotjar / FullStoryContinuous DOM observationLimit to key pages only
Facebook PixelHeavy, fires on every eventLoad via server-side events

For chat widgets specifically: don't load the full widget on page load. Load a lightweight button placeholder, then initialize the real widget only when the user clicks it:

document.getElementById('chat-button').addEventListener('click', () => {
  // Load the real widget JS only on first click
  const script = document.createElement('script');
  script.src = 'https://cdn.example-chat.com/widget.js';
  document.head.appendChild(script);
}, { once: true });

Measuring Improvement

After making changes:

  1. Re-run PageSpeed Insights — the Lab INP score updates immediately. But Lab data is less meaningful for INP than for LCP, because INP is interaction-dependent and the Lab simulates only a fixed set of interactions.

  2. Monitor field data — real INP improvements show up in CrUX data, which PSI surfaces in the Field Data section. This takes 28 days to fully reflect your changes.

  3. Use Chrome UX Report — the CrUX dashboard on Looker Studio shows your INP percentiles over time. The 75th percentile is the one that counts for Core Web Vitals.

For INP issues on Next.js apps specifically, see How to Improve INP on a Next.js App. For an overview of all three Core Web Vitals metrics and how they interact with rankings, see the Core Web Vitals guide.

RankCrab's on-page audit includes INP scores from PageSpeed Insights alongside LCP and CLS, so you can see all three metrics per URL without running PSI manually. If you're tracking multiple WordPress pages, the audit history lets you verify that your deferral and task-splitting changes are actually moving field data in the right direction.

Every Core Web Vitals check, automated.

Lighthouse via Google PSI runs on every audit. Fix the slow page, watch the score climb.