Skip to main content
Practice Problems

Concurrent rendering in React

What is Concurrent Rendering?

Concurrent rendering (React 18+) allows React to interrupt rendering work to handle more urgent updates. Instead of rendering the entire tree synchronously (blocking), React can work on multiple versions of the UI simultaneously, pausing and resuming as needed.


Synchronous vs Concurrent

Before React 18 (Synchronous)

User types β†’ React renders 10,000 items β†’ UI frozen β†’ Input updates ^^^^^^^^^^^^^^^^^^^^^^^^ Can't be interrupted!

React 18+ (Concurrent)

User types β†’ React starts rendering β†’ User types again β†’ React STOPS React restarts with latest input β†’ Renders β†’ UI updates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Interruptible! Prioritized!

Key Concepts

1. Interruptible Rendering

React can pause work on a low-priority update to handle a high-priority one (like user input), then resume later.

2. Automatic Batching

React 18 automatically batches all state updates, even in async functions:

tsx
// React 17 β€” each setState triggers separate re-render in async setTimeout(() => { setCount(c => c + 1); // Re-render 1 setFlag(f => !f); // Re-render 2 }, 1000); // React 18 β€” automatically batched into ONE re-render setTimeout(() => { setCount(c => c + 1); // Batched setFlag(f => !f); // Batched β†’ Single re-render }, 1000);

3. Transitions

Mark non-urgent updates with startTransition:

tsx
import { startTransition } from "react"; // Urgent: show what user typed setInputValue(input); // Non-urgent: filter and display results startTransition(() => { setSearchResults(filterItems(input)); });

4. Suspense for Data Fetching

tsx
function App() { return ( <Suspense fallback={<PageSkeleton />}> <Header /> <Suspense fallback={<ContentSkeleton />}> <MainContent /> {/* Streams when ready */} </Suspense> <Suspense fallback={<CommentsSkeleton />}> <Comments /> {/* Streams independently */} </Suspense> </Suspense> ); }

Concurrent Features Summary

FeatureWhat it doesHook / API
Automatic BatchingBatches all state updatesBuilt-in (React 18)
TransitionsMarks updates as non-urgentuseTransition, startTransition
Deferred ValuesDefers a value updateuseDeferredValue
Streaming SSRSends HTML in chunksSuspense + server
Selective HydrationHydrates parts independentlySuspense + hydration

How to Enable Concurrent Rendering

tsx
// React 18 β€” use createRoot (enables concurrent features) import { createRoot } from "react-dom/client"; const root = createRoot(document.getElementById("root")!); root.render(<App />); // ❌ Legacy β€” ReactDOM.render (no concurrent features) // ReactDOM.render(<App />, document.getElementById("root"));

Practical Benefits

tsx
// Without concurrent rendering: // Typing in search box is laggy because React blocks on filtering 50,000 items // With concurrent rendering: function Search({ items }: { items: Item[] }) { const [query, setQuery] = useState(""); const [isPending, startTransition] = useTransition(); return ( <div> <input value={query} onChange={e => { setQuery(e.target.value); // Urgent: update input startTransition(() => { // Non-urgent: filter setFilteredItems(filterItems(items, e.target.value)); }); }} /> {isPending && <Spinner />} <ItemList items={filteredItems} /> </div> ); } // Input stays responsive! Filtering happens in background.

Important:

Concurrent rendering is opt-in per feature β€” your existing code continues to work. Enable it with createRoot, then use useTransition and useDeferredValue to mark non-urgent updates. React automatically handles batching, interruption, and prioritization. The result: apps that feel more responsive, especially with heavy rendering or slow data.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems