← Clarigital·Clarity in Digital Marketing
Technical SEO · Session 2, Guide 2

INP Optimisation · The Complete Technical Guide

How to diagnose and fix Interaction to Next Paint — Google's responsiveness metric that replaced FID in March 2024. A breakdown of the three INP sub-parts, how to identify long tasks in Chrome DevTools, and specific fixes for every source of interaction latency.

Technical SEO2,900 wordsUpdated Apr 2026

What You'll Learn

  • What INP measures and how it differs from FID (First Input Delay)
  • The three sub-parts of INP: input delay, processing time, and presentation delay
  • What long tasks are and how they block user interaction responsiveness
  • How to break up long tasks using yielding techniques
  • How to reduce JavaScript bundle size and execution time
  • How third-party scripts contribute to poor INP and how to audit them
  • How to diagnose INP problems using Chrome DevTools and real field data

What is INP and How it Differs from FID

Interaction to Next Paint (INP) measures the latency of all user interactions on a page throughout an entire visit and reports the worst-case interaction — or the 98th percentile for pages with many interactions. An "interaction" includes clicks, taps, and keyboard key presses. It does not include scrolling or hovering, which are handled by the browser's compositor thread independently.

INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. FID only measured the delay before the browser started processing the first interaction on a page — it ignored processing time and presentation delay, and only measured one interaction per page visit. A page could have an excellent FID score but sluggish interactions throughout the rest of the session. INP captures the full picture.

Good INP threshold

200ms

75th percentile of interactions must be 200ms or under

Needs improvement

500ms

Above 500ms is classified as Poor — significant user frustration

Long task threshold

50ms

Any main thread task over 50ms can block interaction responses

Sub-part 1

Input Delay

Time from user interaction until the browser starts running the event handlers. Caused by long tasks on the main thread at the time of interaction.

Sub-part 2

Processing Time

Time for event handlers to run. Caused by expensive JavaScript executing synchronously in response to the interaction.

Sub-part 3

Presentation Delay

Time for the browser to render and paint the visual update after event handlers complete. Caused by complex style calculations, layout reflows, or large DOM sizes.

Long Tasks: The Root Cause of Poor INP

A long task is any JavaScript task on the browser's main thread that runs for more than 50ms. The browser cannot respond to user interactions (clicks, taps, keyboard input) while a long task is running — it must finish the current task first. Any interaction that arrives during a long task waits in a queue, accumulating input delay.

Why 50ms is the threshold

Human perception research shows that a response faster than 100ms feels instantaneous. The browser needs approximately 50ms to render a frame at 60fps (16.67ms per frame), so Google established 50ms as the maximum task duration that leaves enough headroom for a frame to be rendered within the 100ms perception window. Tasks longer than 50ms are classified as long tasks in Chrome DevTools.

Common sources of long tasks

  • Large JavaScript bundles executing on load. A 500KB JavaScript bundle may take 3–5 seconds to parse and execute on a mid-range mobile device. Any synchronous code in this bundle that runs on the main thread creates long tasks.
  • Synchronous third-party scripts. Analytics platforms, tag managers, chat widgets, and ad scripts that execute synchronous JavaScript block the main thread while they run.
  • Complex event handlers. onClick or onChange handlers that perform expensive operations — API calls, heavy DOM manipulation, complex calculations — block the main thread during processing.
  • Heavy framework updates. React, Vue, and Angular state updates that trigger large component re-renders can create long tasks during DOM reconciliation.
  • Synchronous storage access. Reading from localStorage synchronously in event handlers blocks the main thread.

Fixing Input Delay

Input delay is reduced by ensuring that long tasks do not occupy the main thread at the moment a user interacts with the page. The primary strategy is breaking up long tasks so the browser has regular opportunities to process pending inputs.

1
Yield to the Main Thread with scheduler.yield()
High Impact

The scheduler.yield() API (Chrome 115+, now in the HTML spec) explicitly yields control back to the browser, allowing it to process pending user inputs before resuming the current task. This is the most modern and recommended approach to breaking up long tasks:

async function processLargeDataset(items) {
  for (let i = 0; i < items.length; i++) {
    processItem(items[i]);
    
    // Yield every 50 items to allow browser to process inputs
    if (i % 50 === 0) {
      await scheduler.yield();
    }
  }
}

For browsers that do not yet support scheduler.yield(), the traditional fallback is await new Promise(resolve => setTimeout(resolve, 0)) — less precise but widely supported.

2
Defer Non-Critical Initialisation Code
High Impact

JavaScript that runs during page load (initialisation of analytics, feature flags, non-critical UI components) creates long tasks that block the main thread. Deferring this code until after the page becomes interactive reduces input delay for early interactions.

// Instead of running immediately on load:
window.addEventListener('load', () => {
  // Defer non-critical work using requestIdleCallback
  requestIdleCallback(() => {
    initAnalytics();
    loadChatWidget();
    prefetchNextPageData();
  });
});

requestIdleCallback schedules work during browser idle periods, preventing it from blocking user interactions.

Reducing Event Handler Processing Time

Processing time is the time your event handlers take to execute in response to an interaction. Reducing it requires auditing what your event handlers actually do and moving expensive work off the synchronous execution path.

1
Move Heavy Work to Web Workers
High Impact

Web Workers run JavaScript on a background thread, completely independent of the main thread. This means complex calculations, data processing, and heavy algorithmic work in event handlers can be moved to a Worker without blocking the UI:

// Main thread
const worker = new Worker('/js/data-processor.js');

button.addEventListener('click', () => {
  // Send data to worker instead of processing synchronously
  worker.postMessage({ action: 'processData', data: largeDataset });
});

worker.onmessage = (e) => {
  updateUI(e.data.result); // Update UI when worker is done
};

Web Workers cannot directly access the DOM but are ideal for computation-heavy tasks: image processing, sorting and filtering large datasets, encryption, and complex calculations.

2
Optimise React and Framework Re-renders
High Impact

In React applications, a single state update can trigger re-renders throughout the component tree if components are not properly memoised. This is one of the most common sources of poor INP in React-based sites.

  • Use React.memo() to prevent re-renders of components whose props have not changed
  • Use useMemo() to cache expensive calculated values between renders
  • Use useCallback() to stabilise function references passed as props
  • Split large components into smaller ones to limit re-render scope
  • Use React 18's concurrent features (useTransition, startTransition) to mark non-urgent state updates as interruptible
3
Avoid Synchronous localStorage and sessionStorage
Medium Impact

localStorage and sessionStorage are synchronous APIs — reading from them blocks the main thread. In event handlers that fire frequently (scroll listeners, input listeners), synchronous storage access can significantly increase processing time. Use IndexedDB (async) for complex data or cache frequently-needed values in memory (JavaScript variables) rather than reading from storage on every event.

Reducing Presentation Delay

Presentation delay is the time between event handlers finishing and the browser completing the next paint. It is often overlooked because the JavaScript processing is complete — yet the user still sees no visual response. It is caused by expensive style recalculation, layout reflow, and paint operations triggered by the DOM changes from event handlers.

1
Reduce DOM Size
High Impact

Large DOM trees (Google recommends keeping total DOM nodes under 1,500) increase the time required for style recalculation and layout. When an event handler modifies the DOM, the browser must recalculate styles and layout for potentially large portions of the tree. Reducing DOM complexity directly reduces presentation delay.

Common causes of excessive DOM size: deeply nested CSS frameworks generating many wrapper elements, rendering long lists without virtualisation, keeping off-screen components mounted in the DOM rather than conditionally rendering them.

2
Avoid Layout-Triggering CSS Properties in Animations
Medium Impact

CSS properties that trigger layout recalculation (width, height, top, left, margin, padding) cause the browser to recalculate positions of affected elements and their siblings. For animations or transitions triggered by user interaction, use only transform and opacity — these are handled by the compositor thread and do not require layout recalculation.

JavaScript Bundle Reduction

The total amount of JavaScript on a page directly correlates with main thread task duration. Every KB of JavaScript must be downloaded, parsed, compiled, and executed — all on the main thread. Reducing JavaScript is therefore one of the highest-leverage INP improvements available.

TechniqueImpactImplementation
Code splittingHighSplit bundles by route or feature so only code needed for the current page loads. Webpack, Rollup, Vite all support code splitting via dynamic import()
Tree shakingHighRemove unused code from bundles. Requires ES modules (import/export) rather than CommonJS (require). Enabled by default in Webpack and Rollup with correct configuration
Remove unused dependenciesHighRun npx depcheck to identify unused npm packages. Replace heavy libraries with lighter alternatives (e.g. day.js instead of moment.js)
Lazy load non-critical componentsMediumUse dynamic import() with React.lazy() or Vue's async components to load non-critical UI components only when needed
Minification and compressionMediumTerser for JS minification; Brotli or gzip compression at the server/CDN level reduces transfer size

Third-Party Scripts and INP

Third-party scripts are among the most significant contributors to poor INP in real-world sites. A Google analysis of HTTP Archive data found that third-party scripts are responsible for a disproportionate share of long tasks on high-traffic websites. Unlike first-party code, third-party script content is outside your direct control — the focus is on isolation, deferral, and removal.

1
Use Facade Patterns for Heavy Embeds
High Impact

A facade is a static placeholder that replaces a third-party embed until the user explicitly interacts with it. Chat widgets, video embeds (YouTube, Vimeo), and map embeds are classic candidates. On page load, display a lightweight static image or placeholder. Only load the actual third-party embed when the user clicks on it:

  • YouTube: Use the YouTube lite embed pattern — show a thumbnail image, load the YouTube iframe only on click. Saves ~500KB of JavaScript per embed.
  • Chat widgets: Show a static "chat" button; load the full widget only when clicked. Most chat platforms support lazy-init APIs.
  • Maps: Show a static map screenshot; load the interactive map only on interaction.
2
Load Tag Manager Tags After Interaction
Medium Impact

Google Tag Manager can fire tags in response to custom events rather than on page load. For analytics, heat mapping, and A/B testing scripts that are not needed for immediate interaction, configure them to fire after the first user interaction (click, scroll) rather than on DOMContentLoaded. This removes their long tasks from the critical page load path.

Measuring and Diagnosing INP

INP is uniquely difficult to measure in lab conditions because it depends on user interactions during a session — synthetic tests that simply load a page cannot capture it. Field data (real user measurements) is essential for understanding your actual INP score.

INP cannot be diagnosed by loading a page — you must interact with it

PageSpeed Insights lab data does not include INP because it requires simulated user interactions. To diagnose INP in Chrome DevTools, you must record a Performance trace and then manually perform the interactions (clicks, taps) that you believe are causing poor INP — then examine the main thread timeline to find long tasks that overlap with or follow those interactions.

Step-by-step INP diagnosis in Chrome DevTools

  1. Open Chrome DevTools → Performance tab
  2. Enable "Web Vitals" checkbox in the toolbar
  3. Click Record, then perform the interaction you suspect is causing high INP (e.g. clicking a button, typing in a field, opening a dropdown)
  4. Stop recording
  5. In the Main thread row, look for long red tasks (tasks over 50ms shown in red)
  6. Find the task that coincides with your interaction — click it to see the call stack
  7. The call stack shows which functions are responsible for the task duration
  8. Look for the three phases: input delay (time before event handler starts), processing time (event handler execution), presentation delay (style/layout/paint after handler)

Use the web-vitals library for field INP data

The web-vitals JavaScript library (github.com/GoogleChrome/web-vitals) can be added to your site to collect real user INP measurements, including the interaction type (click/keypress/tap), the affected element, and the sub-part breakdown. Sending this data to your analytics platform gives you real-world INP data stratified by page, device type, and user geography — far more actionable than lab-only measurements.

Authentic Sources

OfficialGoogle web.dev — Optimise INP

Official INP optimisation guide covering all three sub-parts and improvement strategies.

OfficialGoogle web.dev — Interaction to Next Paint

Official INP definition, how it replaced FID, and threshold values.

OfficialGoogle web.dev — Long Tasks in DevTools

How to identify and diagnose long tasks using Chrome DevTools Performance panel.

OfficialGoogle web.dev — Optimise Long Tasks

Techniques for breaking up long tasks including scheduler.yield() and requestIdleCallback.

OfficialGoogle web.dev — Third-party Facades

Official guidance on facade patterns for reducing third-party script impact on INP.

OfficialGoogle web.dev — DOM Size and Interactivity

How large DOM trees increase presentation delay and how to reduce DOM size.

600 guides on digital marketing. All authentic sources.

Official documentation, academic research, and government data only.