Concurrent Mode in React
Concurrent Mode is a set of foundational capabilities in React that help applications stay responsive and gracefully adjust to the user's device capabilities and network speed. Introduced experimentally and solidified in React 18 as "Concurrent Features", it represents a massive shift in how React approaches rendering.
What is Concurrency in React?
Before Concurrent Mode, React's rendering process was synchronous and blocking. Once React started rendering an update, it could not be interrupted. If a rendering task took a long time (e.g., rendering a huge list or complex charts), the main thread was blocked, meaning the browser couldn't process user inputs, animations stuttered, and the app felt sluggish.
Concurrent React allows the rendering process to be interruptible. React can start rendering an update, pause it in the middle to handle a more urgent task (like a user click or typing in an input), and then resume or discard the previous rendering task.
Key Concepts and Features
1. Time Slicing
Time Slicing is the underlying mechanism that makes Concurrent Mode possible. It allows React to break rendering work into smaller chunks.
Instead of hogging the main thread for 100ms to render a complex tree, React might do 5ms of work, yield control back to the browser to check for user input or paint updates, and then continue working. This ensures the application remains responsive even during heavy CPU tasks.
2. Transitions (startTransition)
Not all state updates are equally urgent. Typing in a search box is urgent—the user expects immediate visual feedback. However, updating the search results below the box is a "transition"—it's okay if it takes a moment, and we don't want it to block the typing animation.
The useTransition hook allows you to explicitly mark certain state updates as non-urgent transitions.
import { useState, useTransition } from 'react';
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
// Urgent update: Update the input field immediately
setQuery(e.target.value);
// Non-urgent update: Fetching and setting results
startTransition(() => {
const newResults = fetchSearchResults(e.target.value);
setResults(newResults);
});
};
return (
<div>
<input type="text" value={query} onChange={handleChange} />
{isPending ? <span>Loading...</span> : <SearchResults data={results} />}
</div>
);
}
3. Automatic Batching
Batching is when React groups multiple state updates into a single re-render for better performance. Prior to React 18, React only batched updates inside React event handlers. Updates inside promises, setTimeout, or native event handlers were not batched.
With React 18's new concurrent root, Automatic Batching is applied everywhere by default.
// React 18 automatically batches these updates into ONE render
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
}, 1000);
4. Suspense Improvements
Concurrent Mode supercharges Suspense. Originally used just for code splitting with React.lazy, Suspense in concurrent React allows you to declaratively wait for any asynchronous data (like API calls) before rendering a component tree.
When a component suspends (because its data isn't ready), Concurrent React can pause rendering that specific subtree and show a fallback, while continuing to render the rest of the application interactively.
Summary
Concurrent Mode is not just a single feature, but a fundamental rewrite of React's core rendering engine. By embracing interruptible rendering, transitions, and automatic batching, developers can build applications that feel significantly faster and more fluid, prioritizing user interaction above all else.