Web Performance

Optimize INP Next.js: Advanced Interaction to Next Paint Fixes

Learn how to diagnose and fix Interaction to Next Paint (INP) issues in Next.js applications using scheduler.yield, React transitions, and web workers.

Krapton Engineering
Reviewed by a senior engineer6 min read
Share
Optimize INP Next.js: Advanced Interaction to Next Paint Fixes

As web applications grow increasingly complex, maintaining a responsive user interface is no longer optional. With Google's Core Web Vitals heavily weighting Interaction to Next Paint (INP) as a key page experience signal, slow user interactions directly translate to dropped search rankings and lost revenue. In modern JavaScript-heavy environments, even minor main-thread blockages can push your INP past the acceptable 200ms threshold.

TL;DR: Optimizing INP in Next.js requires breaking up long tasks that block the main thread during user interactions. By leveraging the Scheduler API, React concurrent features, and offloading heavy computations, you can consistently keep your INP under the 200ms "Good" threshold.

Key takeaways

Wooden Scrabble tiles arranged to spell 'Yes We Can', emphasizing motivation on a white backdrop.
Photo by Brett Jordan on Pexels
  • Yield to the main thread: Use scheduler.yield() to break up long-running JavaScript execution blocks.
  • Leverage React Transitions: Wrap non-urgent state updates in useTransition to prevent UI blocking.
  • Offload heavy tasks: Move non-UI computations, like data parsing or crypto operations, to Web Workers.
  • Audit with Real User Monitoring (RUM): Field data from actual users is the only way to accurately track and fix intermittent INP issues.

Understanding INP and Its Impact on SEO

High-angle view of AMD processors and Noctua thermal paste on a white surface.
Photo by Andrey Matveev on Pexels

Interaction to Next Paint measures a page's overall responsiveness to user inputs like clicks, taps, and keyboard presses. Unlike its predecessor, First Input Delay (FID), which only measured the delay of the very first interaction, INP observes all interactions throughout the entire lifecycle of the page and reports the worst latency. Google defines a "Good" INP score as 200 milliseconds or less.

For Next.js applications, high INP is frequently caused by heavy client-side hydration, monolithic bundle sizes, or long-running event handlers. When a user clicks a button and the main thread is busy executing JavaScript, the browser cannot paint the next frame. This delay creates a sluggish feel that frustrates users and triggers penalties under Google's page experience ranking system.

Diagnosing INP Bottlenecks in Next.js

Before writing code, you must locate the exact tasks blocking the main thread. While lab tools like Lighthouse can flag potential issues, INP is best measured using field data via the Chrome User Experience Report (CrUX) or Real User Monitoring (RUM) libraries like web-vitals.

In a recent client engagement, our team discovered that a critical e-commerce checkout page suffered from an INP of 480ms on mobile devices. Using Chrome DevTools Performance panel, we recorded a user click on the "Add to Cart" button. The flame chart revealed a single monolithic JavaScript task running for 320ms, completely blocking the browser from rendering the updated cart counter. By identifying this long task, we knew exactly where to apply our optimizations.

Breaking Up Long Tasks with scheduler.yield

The most effective way to optimize INP is to break tasks exceeding 50ms into smaller, asynchronous chunks. This allows the browser to interleave rendering and input processing between your JavaScript execution blocks. The modern standard for this is the scheduler.yield() API, which is widely supported in modern browsers.

If you have a complex function that processes a large dataset on the client side, you can yield control back to the browser periodically:

async function processLargeDataset(items: any[]) {
  for (let i = 0; i < items.length; i++) {
    // Process individual item
    transformData(items[i]);

    // Yield to the main thread every 50 items
    if (i % 50 === 0 && 'scheduler' in window && 'yield' in (window as any).scheduler) {
      await (window as any).scheduler.yield();
    }
  }
}

By yielding, the browser can process pending user inputs immediately, ensuring the UI remains responsive even during heavy data processing cycles.

Using React Transitions to De-prioritize State Updates

In Next.js, updating state often triggers expensive component re-renders. If a user types into a search input and that input triggers a heavy filtering operation on a large list, the UI will freeze. We can solve this by splitting state updates into urgent and non-urgent categories using React's useTransition hook.

When you wrap a state update in startTransition, React marks it as low priority. If a user interacts with the page while a transition is rendering, React will pause the transition render, handle the user interaction, and then resume the transition in the background.

import { useState, useTransition } from 'react';

export function SearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (e: React.ChangeEvent) => {
    const value = e.target.value;
    // Urgent: Update the input field value immediately
    setQuery(value);

    // Non-urgent: Filter the heavy list in a transition
    startTransition(() => {
      const filtered = performHeavySearch(value);
      setResults(filtered);
    });
  };

  return (
    
{isPending ?

Loading...

: }
); }

Offloading Computations to Web Workers

When a task is fundamentally too heavy to run on the main thread even with yielding, you should offload it entirely. Web Workers run on a separate operating system thread, meaning they can execute complex algorithms without ever impacting your INP score.

For example, if your Next.js application processes client-side image manipulation, cryptography, or heavy JSON parsing, delegate that work to a worker. In our website development projects, we routinely implement Web Workers for client-side analytical calculations, which keeps the main thread free for immediate visual feedback.

When NOT to Over-Optimize INP

While achieving a perfect INP score is ideal, there are trade-offs to consider. Over-using scheduler.yield() or wrapping every single state update in useTransition can introduce architectural complexity and slightly increase the total time it takes for a process to complete (due to context-switching overhead). If your interactions are already consistently under 100ms on low-end mobile devices, adding complex yielding logic may introduce bugs without providing any perceivable UX or SEO benefit. Focus your optimization efforts strictly on the interactions flagged as slow in your RUM field data.

Comparing INP Optimization Techniques

Different performance bottlenecks require different solutions. The table below outlines when to use each primary optimization strategy:

Optimization Strategy Primary Use Case Implementation Complexity INP Impact
scheduler.yield() Breaking up long, synchronous loops Low High (reduces task duration)
React Transitions De-prioritizing heavy component re-renders Medium Very High (prevents render blocking)
Web Workers Offloading non-UI CPU intensive tasks High Maximum (removes task from main thread)
Debounce/Throttle Limiting event handler execution rate Low Medium (reduces input frequency)

FAQ

How is INP different from FID?

First Input Delay (FID) only measured the delay of the very first interaction on a page. Interaction to Next Paint (INP) measures all interactions throughout the page session and reports the worst lag, making it a much more accurate reflection of real-world user experience.

Can I optimize INP by optimizing my Next.js bundle size?

Yes. Smaller bundles mean the browser spends less time parsing and compiling JavaScript during page load, which prevents the main thread from being locked up when a user tries to interact with the page early in its lifecycle.

How do I test INP locally?

You can test INP locally using Chrome DevTools. Open the Performance panel, enable CPU throttling (e.g., 4x or 6x slowdown to simulate a mobile device), record a session, and interact with your page. Look for long tasks highlighted with red flags.

Partner with Krapton for Core Web Vitals Audits

Optimizing Core Web Vitals requires deep diagnostic expertise and a systematic approach to performance engineering. If your Next.js site is struggling with slow interaction speeds or failing Google's page experience checks, our team can help you identify and resolve the root causes.

Ready to boost your organic traffic and conversion rates? You can run a free SEO audit with Krapton's SEO Analyzer to diagnose your site's LCP, INP, and CLS scores instantly, or reach out to hire Next.js developers from our dedicated engineering team to implement these advanced optimizations for you.

About the author

The Krapton Engineering team consists of principal-level software engineers and performance specialists who design, build, and optimize high-traffic web applications, SaaS platforms, and mobile products for global startups and enterprises.

core web vitalsweb performanceinpnext.js performancepage speedseo performance
About the author

Krapton Engineering

The Krapton Engineering team consists of principal-level software engineers and performance specialists who design, build, and optimize high-traffic web applications.