All topics
Advancedadvanced

Concurrent Rendering Mental Model

Understand the shift from synchronous to concurrent rendering in React 18 and its implications for render purity.

React 18 introduced Concurrent Rendering as an opt-in capability (enabled by using the new createRoot API rather than the legacy ReactDOM.render), fundamentally changing rendering from an all-or-nothing synchronous operation into an interruptible process that React can pause, resume, or even discard partway through — made possible by the Fiber architecture's unit-of-work model.

Concurrent rendering is like a chef who can pause mid-recipe to handle a rush order that just came in, and either resume the paused dish exactly where they left off or, if the original order changed while they were interrupted, just start that dish over completely — which is fine as long as nothing about preparing the dish has an irreversible side effect that shouldn't happen more than once, like an ingredient that can't be un-added if the dish gets restarted.

Key Concepts

1
The practical implication most developers encounter first is that React may call a component's function body more than once for a single logical render, without necessarily committing the result of every call — for example, rendering a component, pausing to handle something more urgent, and restarting that render from scratch afterward. This makes it essential that render logic remain a pure function of props and state, since any side effect performed directly during rendering (not inside an effect) could run multiple times per actual visible update, or run for a render that ultimately gets discarded entirely.
2
Concurrent features (useTransition, useDeferredValue, Suspense-based data loading) are all built on top of this interruptible foundation, letting React prioritize urgent updates (typing, clicking) over less urgent ones (a big background recompute), yielding to the browser periodically so long renders don't block the main thread and cause visibly janky, unresponsive interactions.
useTransitionuseDeferredValue
3
Interviewers exploring this topic ask candidates why 'components must be pure during render' became a more strictly enforced expectation with React 18, and expect an answer connecting it directly to concurrent rendering's ability to render a component more than once (or discard a render entirely) — a side effect during render that used to 'just happen once' under the old synchronous model can now visibly misbehave (double logging, double-incrementing a module-level counter) under concurrent rendering.