Concurrent React is the biggest architectural shift since hooks. At Google, we've adopted these patterns to build fluid, responsive UIs.
What is Concurrent Rendering?
In traditional React, rendering is synchronous and blocking. Concurrent mode lets React pause, interrupt, and resume rendering. This means urgent updates (like typing) aren't blocked by expensive renders (like filtering a large list).
useTransition
function SearchResults({ query }) {
const [isPending, startTransition] = useTransition();
const [results, setResults] = useState([]);
function handleSearch(input) {
// Urgent: update the input field
setQuery(input);
// Non-urgent: update results
startTransition(() => {
setResults(filterLargeList(input));
});
}
return (
<div>
<input onChange={e => handleSearch(e.target.value)} />
{isPending ? <Spinner /> : <ResultList items={results} />}
</div>
);
}useDeferredValue
Like useTransition but for values instead of state updates. The deferred value lags behind the actual value during heavy renders.
function List({ input }) {
const deferredInput = useDeferredValue(input);
const items = useMemo(() => filterItems(deferredInput), [deferredInput]);
return <ul>{items.map(renderItem)}</ul>;
}Suspense for Data Fetching
Suspense lets components "wait" for async data. Instead of loading states in every component, you declare loading boundaries:
<Suspense fallback={<Skeleton />}>
<UserProfile />
<UserPosts />
</Suspense>Production Gotchas
- Double rendering in dev: Concurrent mode intentionally double-renders to catch side effects. Don't panic.
- Tearing: External stores (Redux, Zustand) must use
useSyncExternalStoreto avoid tearing - Suspense waterfalls: Fetch in parallel, not sequentially. Use Promise.all or a data layer like React Query