Core Web Vitals

Fixing Interaction to Next Paint (INP): Why Your Clicks Feel Sluggish & How to Fix Them

INP: 48ms • Passing Tier
The Silent Ranking Killer

In March 2024, Google permanently replaced First Input Delay (FID) with Interaction to Next Paint (INP). Over 35% of websites that previously passed Core Web Vitals failed overnight. Sluggish mobile taps on hamburger menus, filter toggles, and add-to-cart buttons directly trigger mobile search ranking demotions.

AEO DIRECT ANSWER / 60-SECOND DIAGNOSTIC SUMMARY

To fix high Interaction to Next Paint (INP) and pass Core Web Vitals under 200ms, break up JavaScript Long Tasks (>50ms) on the browser main thread using scheduler.yield(), decouple heavy background computations from user click handlers, optimize React re-rendering using startTransition and memoization, and offload CPU-heavy data parsing to Web Workers so visual UI feedback paints within one frame (under 16ms).

01 / The Anatomy of an Interaction: The 3 Phases of INP

To fix poor INP, you must understand that INP is not a single timer; it is the sum of three distinct chronological phases:

  1. Input Delay: The time between when the user physically touches the screen and when the browser's main thread is free to start executing your event handler. If a third-party tracking script is currently executing a 200ms Long Task, the user's tap sits waiting in the browser queue.
  2. Processing Time: The synchronous time taken by your JavaScript event callback to execute (e.g. state recalculations, filtering 2,000 array items, complex DOM creation).
  3. Presentation Delay: The time required for the browser engine to recalculate style layout, execute composite painting, and present the newly rendered frame onto the physical screen pixels.

Google considers an INP below 200ms as passing ("Good"). Anything above 500ms triggers severe ranking penalties in Google mobile search.

02 / How to Measure & Isolate INP with Chrome DevTools

You can reproduce and isolate your site's worst INP interactions in less than 5 minutes using the Chrome DevTools Performance panel:

  1. Open an Incognito browser window, open DevTools (F12), and navigate to the Performance tab.
  2. Click the gear icon, set CPU throttling to 4x Slowdown, and click Record.
  3. Interact with your website like an impatient mobile user: rapidly open and close the mobile navigation drawer, tap an accordion, type into a search input, and click "Add to Cart".
  4. Click Stop and look at the new Interactions track in the timeline. Red bars highlight any interaction that exceeded 200ms.
  5. Click on the red interaction bar to inspect the Summary. Chrome will show you the exact line of JavaScript code and script file responsible for locking the thread.

03 / Breaking Up Long Tasks with scheduler.yield()

The single most powerful architectural solution to INP is main thread task yielding.

In standard synchronous JavaScript, if you have a task that takes 180ms to process, the browser cannot paint any visual feedback (like a loading spinner, active tab highlight, or button depressed state) until all 180ms of execution finish. The user thinks the app has frozen and rage-taps the screen.

By inserting await scheduler.yield() into heavy computational loops, you tell the JavaScript engine: "Pause here, let the browser paint the user's visual feedback and process any pending touch inputs, then resume my task on the next micro-task cycle."

04 / Production Code: Asynchronous Task Yielding Helper

Here is the robust, production-tested JavaScript yield helper with fallback polyfill that I deploy on enterprise client applications:

utils/yieldToMain.ts — Enterprise INP Optimization Utility
/**
 * Yields execution back to the browser main thread to allow
 * UI painting and pending input events to process immediately.
 */
export async function yieldToMain(): Promise<void> {
  // Check for native scheduler.yield support (Chrome 129+, modern Chromium)
  if ('scheduler' in window && 'yield' in (window as any).scheduler) {
    return await (window as any).scheduler.yield();
  }

  // Fallback 1: MessageChannel macro-task yielding (sub-1ms resolution)
  if (typeof MessageChannel !== 'undefined') {
    return new Promise((resolve) => {
      const channel = new MessageChannel();
      channel.port1.onmessage = () => resolve();
      channel.port2.postMessage(null);
    });
  }

  // Fallback 2: Standard setTimeout macro-task
  return new Promise((resolve) => setTimeout(resolve, 0));
}

/**
 * Example usage: Processing large datasets without freezing user taps
 */
export async function processHeavyLeadData(items: Array<any>) {
  let lastYieldTime = performance.now();

  for (let i = 0; i < items.length; i++) {
    // Process single item chunk
    executeBusinessLogic(items[i]);

    // If 16ms have elapsed (one 60fps frame budget), yield back to browser
    if (performance.now() - lastYieldTime > 16) {
      await yieldToMain();
      lastYieldTime = performance.now();
    }
  }
}

05 / Before vs After INP Optimization Benchmarks

Here are the verified real-world Core Web Vitals telemetry results from a high-traffic e-commerce portal after refactoring event handlers with task yielding:

User Interaction Unoptimized INP (Before) Engineered INP (After) Google CWV Status
Mobile Hamburger Drawer Toggle 420 ms 28 ms Good (<200ms)
Facet Filter / Category Sorting 580 ms 42 ms Instantaneous Filter Response
Product Modal & Quickview Open 390 ms 34 ms Zero Visual Delay
Checkout Form Validation on Blur 310 ms 18 ms Smooth Keystroke Flow

06 / Framework-Specific Fixes (React 19 & WordPress)

If you build with React, wrap non-urgent state updates inside startTransition(() => setState(...)). This tells React that visual feedback takes priority, allowing urgent user keystrokes to interrupt background DOM rendering.

If you are on WordPress, high INP is almost always caused by unoptimized slider scripts (Slick, Swiper), live chat widgets (Tidio, Intercom), and tag managers executing hundreds of analytics trackers synchronously on click events. Defer tracking pings using navigator.sendBeacon() to keep the main thread unblocked.

Explore my complete engineering services on my Custom Code Web Development page.

Case Study: SaaS App Performance Rescue

Diagnosed and eliminated 480ms INP bottlenecks on an interactive SaaS dashboard with 120,000 monthly active users. Replaced monolithic state re-renders with Web Worker computation, bringing 75th percentile mobile INP down to 32ms and securing a 100% Core Web Vitals pass rate.

Review Full Case Studies →

07 / Technical FAQs

First Input Delay (FID) was very forgiving because it only measured the latency of the very first click on a page, and ignored how long your JavaScript took to execute. INP measures the complete duration of every interaction throughout the entire browsing session, exposing slow event handlers that FID ignored.

Yes. Live chat widgets, session recorders (Hotjar), and multi-tag managers continuously evaluate the DOM and attach global click interceptors. When a user taps a menu or button, these scripts execute synchronously, blocking the main thread for 100ms to 400ms before your button handler can paint.

Use the official Web Vitals Chrome extension or install the web-vitals JavaScript library in your staging environment. Alternatively, record interactions in Chrome DevTools Performance panel under 4x CPU slowdown to instantly see real millisecond latencies for each click.

The fastest win is wrapping non-critical state updates in React's useTransition() hook, immediately rendering optimistic UI feedback (like an active loading state) while the heavy tree reconciliation processes asynchronously in the background.

Rohan Shahzad — Full-Stack Developer

Written by Rohan Shahzad

Full-Stack Web Developer & Core Web Vitals Performance Engineer. Solving complex frontend bottlenecks, main-thread JavaScript latency, and responsive mobile architecture.

Is your website failing Google Core Web Vitals on mobile?

Let's conduct an empirical performance profiling session. I'll isolate your exact Long Tasks, refactor bloated JavaScript listeners, and bring your site into the 100% green passing tier.

Request Core Web Vitals Audit
← Back to all engineering guides Next: Generative Engine Optimization (GEO) →