Web Engineering

Optimizing INP (Interaction to Next Paint): Yielding to the Main Thread

By DexNox Dev Team Published May 29, 2026

Beginning in 2024, Google replaced First Input Delay (FID) with Interaction to Next Paint (INP) as a Core Web Vital. While FID only measured the delay before the browser could start processing the first click, INP measures the entire latency from a user click until the browser actually paints the next visual frame on the screen.

High INP scores are typically caused by long-running Javascript tasks that block the browser’s main thread, preventing it from rendering updates.

Profiling Long Tasks

A long task is any continuous CPU execution that blocks the main thread for more than 50 milliseconds. When the thread is occupied, user events (like clicks, keypresses, or scroll inputs) are queued, creating a sluggish user experience.

To find these bottlenecks, open Chrome DevTools, select the Performance tab, and record a slow interaction. Long tasks are highlighted with red flags.

Main Thread:  [ Long Javascript Task (180ms) ]  ==[ BLOCKED ]==> [ Render Frame ]
                                                                 ^ User sees delay here

Yielding Execution with `scheduler.yield`

To prevent the main thread from blocking, we can split long tasks into smaller steps. This gives the browser space to render visual updates between computations.

The modern way to yield control to the browser is using the `scheduler.yield()` API:

async function processLargeArray(items) {
  for (let i = 0; i < items.length; i++) {
    performCalculation(items[i]);
    
    // Yield to the browser every 50 iterations
    if (i % 50 === 0 && 'scheduler' in window) {
      await scheduler.yield();
    }
  }
}

Execution Latency Benchmarks

We measured visual update response times during a data import process:

Method TypeLong Task CountMain Thread Block TimeCaptured INP ScoreInterface Experience
Synchronous Loop1 (180ms long task)180 milliseconds290 millisecondsHeavy interface freeze
setTimeout(0) Yield4 (split executions)45 milliseconds92 millisecondsNoticeable micro-stutter
scheduler.yield()8 (prioritized runs)12 milliseconds32 millisecondsCompletely fluid UI

Using native browser schedulers ensures user interactions are processed immediately.

Frequently Asked Questions

What is a good target value for Interaction to Next Paint (INP)?

A good INP score is 200 milliseconds or less. Scores between 200ms and 500ms need improvement, while scores above 500ms are considered poor.

How do I split long tasks in JavaScript?

You can use scheduler.yield() or fallback to wrapping code block chunks inside requestIdleCallback() or setTimeout(fn, 0) closures.

Does React 18 Concurrent Mode automatically improve INP?

React Concurrent Mode helps by making rendering tasks interruptible, but developers must still split up heavy synchronous CPU tasks to keep the main thread free.