Concurrent Rendering — How It Works
React 18's concurrent mode fundamentally changes how React renders. Instead of synchronous, blocking renders, concurrent rendering is interruptible — React can pause a render in progress, handle a more urgent update (like a keypress), then resume. This is the foundation for all React 18 features.
Concurrent Mode Internals
- React 18 uses a Fiber architecture — every component becomes a 'unit of work'. Between fibers, React checks if higher-priority work (user input) is pending
- Synchronous render (pre-React 18): React locks the main thread until the entire tree is rendered — a 200ms render makes the page feel frozen
- Concurrent render: React time-slices work into small chunks (~5ms each). Between chunks, it yields to the browser event queue. User inputs are always processed promptly
- Two phases — Render phase (pure, interruptible, may be run multiple times): computes what changed. Commit phase (side effects, DOM mutations, synchronous, non-interruptible): applies changes
- Transition updates vs urgent updates: typing, clicking = urgent (synchronous). Navigation, filtering, loading new data = transition (can be interrupted). This distinction is the key to concurrent React
- createRoot() enables concurrent features — old ReactDOM.render() is legacy mode. New projects always use createRoot
- Automatic batching: React 18 batches ALL state updates (including those inside setTimeout, promises, and event handlers) into a single render. Previously, only React event handlers were batched
Automatic Batching — React 18
// React 17 — no batching in async code
setTimeout(() => {
setCount(c => c + 1); // causes re-render
setFlag(!flag); // causes another re-render
// 2 separate renders
}, 100);
// React 18 — automatic batching everywhere
setTimeout(() => {
setCount(c => c + 1);
setFlag(!flag);
// 1 render (batched automatically)
}, 100);
// Also batched in React 18:
fetch('/api/data').then(() => {
setData(result); // }
setLoading(false); // } — one render
});
// Opt out of batching (rare — sometimes needed)
import { flushSync } from 'react-dom';
flushSync(() => setCount(c => c + 1)); // re-renders immediately
flushSync(() => setFlag(!flag)); // re-renders immediatelyTip
Tip
Practice Concurrent Rendering How It Works in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
useTransition for search/filter. useDeferredValue for expensive renders. Suspense for async. All automatic in React 18+.
Practice Task
Note
Practice Task — (1) Write a working example of Concurrent Rendering How It Works from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Concurrent Rendering How It Works is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready react code.
Key Takeaways
- React 18's concurrent mode fundamentally changes how React renders.
- React 18 uses a Fiber architecture — every component becomes a 'unit of work'. Between fibers, React checks if higher-priority work (user input) is pending
- Synchronous render (pre-React 18): React locks the main thread until the entire tree is rendered — a 200ms render makes the page feel frozen
- Concurrent render: React time-slices work into small chunks (~5ms each). Between chunks, it yields to the browser event queue. User inputs are always processed promptly