← Back to Chapters

Concurrent Rendering

⚡ Concurrent Rendering

⚛️ Introduction

Concurrent Rendering is one of the core innovations in React 18+. It allows React to prepare multiple versions of the UI simultaneously — making updates smoother, faster, and more interruptible.

Instead of rendering everything synchronously and blocking the UI, React can now pause, resume, or even cancel rendering tasks based on priority.

? The Problem (Before React 18)

In React 17 and earlier, rendering was synchronous. When a heavy component rendered, it blocked the main thread — freezing the UI until completion.

? View Code Example
// React 17: Legacy behavior
setState(heavyData); // causes noticeable lag

This could make typing, animations, or other interactions feel sluggish because the browser couldn't process input events until the render finished.

⚙️ How It Works

React 18 introduces a concurrent renderer that works asynchronously. It can:

  • Pause rendering in the middle if something urgent (like typing) happens.
  • Reuse partial work already done.
  • Prioritize updates (e.g., user input over background data).

This is made possible through new concurrent APIs like useTransition, useDeferredValue, and startTransition.

? Visualizing Concurrency

Legacy (Sync): ? Large Render Block (User waits) Concurrent: Input Render is sliced & interruptible

? Slow Rendering Simulation

Here’s a simple example showing how concurrent rendering keeps the UI responsive while rendering heavy data.

? View Code Example
// 1. Import useTransition for concurrency control
import React, { useState, useTransition } from "react";

function SlowList({ text }) {
  // Intentionally heavy operation: creating 3000 items
  const items = Array(3000).fill(0).map((_, i) => <li key={i}>{text} - Item {i}</li>);
  return <ul>{items}</ul>;
}

export default function ConcurrentExample() {
  const [input, setInput] = useState("");
  const [list, setList] = useState([]);
  
  // 2. Initialize the hook
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    const value = e.target.value;
    
    // 3. High Priority: Update input immediately so typing feels fast
    setInput(value); 

    // 4. Low Priority: Wrap heavy updates in startTransition
    startTransition(() => {
      setList(Array(3000).fill(value));
    });
  };

  return (
    <div className="p-3">
      <input
        className="form-control mb-2"
        value={input}
        onChange={handleChange}
        placeholder="Type something..."
      />

      // 5. Show loading state if transition is pending
      {isPending ? <p>Updating list...</p> : <SlowList text={input} />}
    </div>
  );
}

? Interactive Simulation

This demo simulates how the Browser Main Thread handles heavy work.
Instruction: Click a button below, then immediately try to type in the input box.

Status: Idle
 

? Synchronous vs Concurrent

Aspect React 17 (Synchronous) React 18 (Concurrent)
Rendering Model Blocking, single-threaded Interruptible, cooperative
Performance Freezes on heavy updates Smooth, responsive UI
Prioritization No priority distinction High vs low priority updates
Developer APIs Legacy updates only useTransition(), startTransition()

? Key Benefits

  • Interruptible Rendering: React can pause rendering if the user interacts.
  • Prioritized Updates: Urgent updates render first (like typing).
  • Smoother UI: No more noticeable lag on heavy components.
  • Seamless Integration: Works automatically with React 18 APIs.

? Tips

  • Use useTransition() for deferred updates (like filters, lists).
  • Don’t overuse concurrent features — React automatically handles priorities for standard state updates.
  • Combine concurrent rendering with Suspense for best async UX.
  • Profile renders using the React DevTools Profiler to see interruptions.

? Try This

  1. Build a large list that filters as you type — with and without useTransition().
  2. Simulate slow computation in rendering to observe React pausing/resuming updates.
  3. Inspect the flame chart in the React Profiler to see render interruption behavior.
  4. Combine with Suspense and observe how it keeps UI responsive during async fetches.

Goal: Understand how concurrent rendering helps React handle heavy or async updates while maintaining a fluid user experience.