MEET MIKE
Back to Knowledge Hub
Optimization & RestructuringTarget: CTOs

How to Diagnose and Fix Interaction to Next Paint (INP) in Complex React Applications

A practical guide to improving Interaction to Next Paint (INP) in React using concurrent rendering, long-task optimization, and main-thread scheduling.

MA
Michael AdedapoSenior Full Stack & Systems Engineer
March 5, 20269 min read
#INP#Core Web Vitals#React 18
Share:

System Architecture & Request Execution Pipeline

Visual Diagram
Client RequestEdge CDN DNS
Server LogicNext.js Execution
Optimization LayerSub-Second Delivery
Final UX PaintCore Web Vitals Pass

Visual architecture flow for: How to Diagnose and Fix Interaction to Next Paint (INP) in Complex React Applications

Google's Interaction to Next Paint (INP) is now one of the three Core Web Vitals that directly measures how responsive your application feels to real users.

Unlike First Input Delay (FID), which measured only the delay before an event handler began executing, INP measures the entire interaction lifecycle—from the moment a user clicks, taps, or types until the updated interface is visibly rendered.

For complex React applications, poor INP usually isn't caused by React itself. It's caused by blocking the browser's main thread with expensive JavaScript, unnecessary re-renders, and inefficient rendering pipelines.

Understanding the INP lifecycle

Every interaction consists of three measurable phases.

Input Delay Waiting
CODE
<rect x="123" y="26" width="94" height="34" rx="6" fill="#FDE68A" stroke="#D97706"/>
<text x="170" y="38" fontWeight="bold" fill="#92400E">Processing</text>
<text x="170" y="48" fontSize="7" fill="#92400E">JavaScript</text>

<rect x="234" y="26" width="94" height="34" rx="6" fill="#D1FAE5" stroke="#059669"/>
<text x="281" y="38" fontWeight="bold" fill="#047857">Paint</text>
<text x="281" y="48" fontSize="7" fill="#047857">Updated UI</text>

<path d="M106 43 L123 43" stroke="#64748B" strokeDasharray="3 3"/>
<path d="M217 43 L234 43" stroke="#64748B" strokeDasharray="3 3"/>
PhaseWhat happens
Input DelayBrowser waits for the main thread to become available
Processing TimeReact executes your event handler and state updates
Presentation DelayBrowser performs layout, paint, and displays the new frame

Google considers an INP of 200ms or less (75th percentile) to provide a good user experience.


Why React apps fail INP

Most slow interactions fall into one of four categories:

  • Long synchronous event handlers
  • Expensive list filtering or sorting
  • Large component trees re-rendering unnecessarily
  • Layout recalculation after state changes

The objective is not reducing clicks—it's reducing the amount of work performed before the browser can paint.

1. Avoid blocking the main thread

A common anti-pattern is performing expensive computation directly inside an event handler.

❌ Blocking interaction

TSX
function ProductFilter({ products, category }) {
  const [results, setResults] = useState(products)

  function handleFilter() {
    const filtered = heavyFilterAndSort(products, category)
    setResults(filtered)
  }

  return <button onClick={handleFilter}>Filter</button>
}

The user clicks, but the interface cannot update until filtering completes.

✅ Concurrent rendering with useTransition

TSX
import { useState, useTransition } from "react"

function ProductFilter({ products, category }) {
  const [results, setResults] = useState(products)
  const [isPending, startTransition] = useTransition()

  function handleFilter() {
    startTransition(() => {
      const filtered = heavyFilterAndSort(products, category)
      setResults(filtered)
    })
  }

  return (
    <button onClick={handleFilter} disabled={isPending}>
      {isPending ? "Filtering..." : "Filter"}
    </button>
  )
}

startTransition() tells React that this update is non-urgent, allowing visual feedback to appear immediately while the expensive update is scheduled.


2. Break long tasks into smaller chunks

Any JavaScript task exceeding 50ms is considered a long task and can negatively impact responsiveness.

Instead of processing thousands of records continuously, periodically yield control back to the browser.

TypeScript
export const yieldToMain = () =>
  new Promise(resolve => setTimeout(resolve, 0))

async function processLargeDataset(rows) {
  const output = []

  for (let i = 0; i < rows.length; i++) {
    output.push(transform(rows[i]))

    if (i % 250 === 0) {
      await yieldToMain()
    }
  }

  return output
}

This allows the browser to render loading indicators and remain responsive during intensive work.


3. Eliminate unnecessary re-renders

Even fast event handlers can produce poor INP if they trigger large rendering cascades.

Component isolation strategy

Parent State
CODE
<path d="M170 32 V 44" stroke="#64748B" strokeDasharray="3 3"/>
<path d="M170 44 L90 58" stroke="#64748B"/>
<path d="M170 44 L250 58" stroke="#64748B"/>

<rect x="35" y="58" width="110" height="26" rx="6" fill="#FECACA" stroke="#DC2626"/>
<text x="90" y="70" fontWeight="bold" fill="#991B1B">Without Memo</text>
<text x="90" y="78" fontSize="7" fill="#991B1B">1,000 rows render</text>

<rect x="195" y="58" width="110" height="26" rx="6" fill="#D1FAE5" stroke="#059669"/>
<text x="250" y="70" fontWeight="bold" fill="#047857">React.memo</text>
<text x="250" y="78" fontSize="7" fill="#047857">Changed rows only</text>

<path d="M90 84 V 96" stroke="#FCA5A5" strokeDasharray="2 2"/>
<path d="M250 84 V 96" stroke="#86EFAC" strokeDasharray="2 2"/>

<text x="90" y="108" fontSize="7" fill="#991B1B">Large reconciliation</text>
<text x="250" y="108" fontSize="7" fill="#047857">Minimal updates</text>

Memoization becomes valuable when:

  • Lists exceed hundreds of items
  • Parent state changes frequently
  • Child props remain stable

Use React.memo, useMemo, and useCallback intentionally—not everywhere.


4. Profile interactions before optimizing

Chrome DevTools provides the clearest picture of INP bottlenecks.

Look for:

  • Long Tasks (red blocks)
  • Large scripting sections
  • Forced layout events
  • Repeated React commits

The Performance panel usually reveals whether the bottleneck is JavaScript or rendering.

INP benchmark reference

INP ScoreRatingUser Experience
0–200msGoodImmediate, fluid interactions
201–500msNeeds ImprovementNoticeable lag
Over 500msPoorUI feels frozen or delayed

Remember that INP is measured from real user interactions, not synthetic laboratory clicks.


Practical optimization checklist

  • Measure INP with Real User Monitoring (RUM)
  • Identify long tasks over 50ms
  • Wrap non-urgent updates in startTransition()
  • Split expensive computations into asynchronous chunks
  • Memoize stable components and callbacks
  • Virtualize large tables and lists
  • Reduce unnecessary DOM depth during interactive updates

Improving INP is often about doing less work, not writing more code.


Final thoughts

A fast application is one that responds immediately to human input. React 18's concurrent rendering model, combined with disciplined component architecture and careful main-thread scheduling, makes it possible to deliver interfaces that consistently feel responsive under real-world load.

Rather than chasing Lighthouse scores alone, optimize the interactions users perform hundreds of times every day—that is where INP creates measurable product value.

Verified Benchmarks

Verified Client Benchmark Impact

Performance & business outcomes achieved after applying these architecture principles

Page Load Speed (LCP)83% Faster
Before4.8s
After0.8s
Search Engine VisibilityTop 3 Rankings
Before+150%
After+380%
User Lead Conversions+200% Growth
Before1.4%
After4.2%
Real Work Case Study
Client: Growth Scale Client (Optimization & Restructuring)
The Challenge

High bounce rate and slow page transitions impacting CTOs.

The Solution

Refactored core web architecture into modern Next.js edge-rendered static structures with streamlined UX.

Key Measurable Outcomes
  • Core Web Vitals passed 100% on mobile and desktop
  • Inbound customer inquiries increased 3x within 30 days
  • Zero downtime recorded post-launch

Enjoyed this article?

Share it with your network or colleagues

Share:
Complimentary Technical Audit

Ready to Apply These Insights to Your Own Web Infrastructure?

Get a tailored 20-point technical, performance, and security audit of your web application.

100% Free - No ObligationDirect 1-on-1 Strategy Session
Book Your Free Technical Audit

Only 3 slots available this week