CONCEPT Cited by 2 sources
Concurrent rendering (React 18)¶
Concurrent rendering is the React 18 feature set that decouples
deciding what to render from committing it to the DOM, allowing
the renderer to pause, resume, prioritise, and interleave render
work across multiple trees. In concrete API surface: <Suspense>
boundaries, renderToPipeableStream / renderToReadableStream
(streaming SSR), hydrateRoot (incremental client hydration),
useTransition (non-blocking state updates), useDeferredValue,
and onRecoverableError (error reporting that doesn't crash the
tree).
React's older model rendered synchronously — once a render started it ran to completion, blocking the event loop (on the server) or the main thread (in the browser) until done. Concurrent rendering makes rendering interruptible: boundaries commit when ready, hydration happens per-boundary, transitions yield to user input.
Named primitives¶
<Suspense>boundaries — declarative "this subtree may not be ready yet" markers. The unit of streaming/hydration granularity. See patterns/suspense-boundary.renderToPipeableStream/renderToReadableStream— streaming SSR APIs that emit HTML per-boundary as each becomes ready, replacingrenderToNodeStream/renderToString.hydrateRoot— new client hydration entry point, replacinghydrate. Does per-boundary hydration, supportsonRecoverableErrorto report mismatches without aborting.useTransition/startTransition— mark a state update as non-urgent so React can interrupt it if a higher-priority render comes in (user typing, etc).useDeferredValue— similar shape: a state-copy that lags behind, letting the urgent render finish first.onRecoverableError— a callback onhydrateRootfor hydration-mismatch reporting. Replaces the React-17 behaviour of silently dropping to client-rendering with an error on the console.suppressHydrationWarning={true}— per-element opt-out of hydration-mismatch checking, for content that is intentionally different SSR vs CSR (timers, device-specific content). See patterns/suppress-hydration-warning-for-unavoidable-mismatch.
The architectural shift¶
React 17 and earlier:
SSR: render whole tree → string → emit → done
Hydration: one hydrate() call on the whole tree → done
React 18 concurrent:
SSR: render → commit boundary A → emit chunk A
→ resolve boundary B's data
→ commit B → emit chunk B
→ ...
Hydration: hydrate shell → boundary A ready → hydrate A
→ boundary B ready → hydrate B
→ ...
The renderer is the same React tree; what changes is that commit is per-boundary, not whole-tree, on both sides.
Why RE-style frameworks fit naturally¶
Zalando's Rendering Engine (Source: sources/2023-07-10-zalando-rendering-engine-tales-road-to-concurrent-react) had, pre-React-18, already built:
- Partial hydration — hydrate what needs to be interactive, skip the rest.
- Partial streaming — emit ready regions of the page while later ones are still pending.
- Lazy-loading of Renderers.
The Zalando post frames React 18 adoption as "[RE]'s Renderers serve as ideal candidates for being encapsulated within a Suspense boundary. This enables them to function as individual blocks that can be server-rendered, streamed, hydrated, and client-rendered 'concurrently'. Especially since many of them have already been using Rendering Engine's own partial hydration/streaming features!" (Source: same.) So the work is more about delegating already-built capabilities to a supported upstream implementation than about adopting new ones.
This is a general property: any framework that composes a tree of independently-resolvable sub-views (micro-frontends, entity-based composition, component-library-with-async-data, etc.) maps naturally onto Suspense-as-streaming/hydration-unit.
Trade-offs¶
- Wins: incremental paint and TTI, better INP on large component trees, no-whole-tree-re-render on slow-boundary data, error isolation per boundary, natural fit for progressive hydration.
- Costs: stricter hydration contract surfaces latent SSR/CSR divergences that React 17 silently papered over (see concepts/hydration-mismatch). Adopting the concurrent APIs without fixing those produces a worse UX than the React 17 baseline (hydration errors → full CSR fallback → Sentry noise).
- Hook-based "Render-As-You-Fetch" (the obvious pattern) has
known sharp edges —
SuspenseListexperimental,useTransitionnot nested-boundary aware, fetch timing coupled to render order, client-side data cache not finalised. Zalando rejected it for RE; see concepts/render-as-you-fetch and patterns/application-state-layer-outside-react.
Measured impact of the narrow API swap¶
Zalando rolled out just renderToPipeableStream + hydrateRoot
— no other concurrent-feature adoption — as an A/B test on
Fashion Store and measured
(Source: sources/2023-07-10-zalando-rendering-engine-tales-road-to-concurrent-react):
- INP −5.69 % overall / −6.76 % Catalog / −6.09 % PDP.
- FID −8.81 % overall / −17.11 % Catalog / −6.06 % PDP.
- LCP −2.43 % / FCP −0.23 %.
- Bounce −0.24 % overall.
Read: the interactive-latency metrics (INP,
FID) improve most — consistent with hydrateRoot doing less
synchronous work than hydrate. Paint metrics (LCP, FCP) improve
less because the narrow swap doesn't restructure when data
resolves.
Seen in¶
- sources/2023-07-10-zalando-rendering-engine-tales-road-to-concurrent-react — canonical wiki instance of a rejection of pure Render-As-You-Fetch in favour of an externally-dictated Suspense state. Zalando keeps React as the renderer but puts their Application-State layer outside React and uses a Connector hook that returns either data or a Promise to drive suspension. Also the wiki's canonical source for the A/B-test delta of the narrow API swap.
- sources/2026-04-16-atlassian-streaming-ssr-confluence —
Atlassian Confluence's React 18 streaming SSR via
renderToPipeableStream+<Suspense>, including the React-18useContext-change-across-ready-boundary bug (fixed in 19) and the required state-injection-ordering discipline for hydration.
Related¶
- concepts/streaming-ssr — what concurrent rendering enables on the server side.
- concepts/react-hydration — what it enables on the client side.
- concepts/progressive-hydration — hydrating-per-boundary as a direct consequence.
- concepts/render-as-you-fetch — the hook-based data pattern concurrent rendering is designed for; not always the right fit.
- patterns/suspense-boundary — the unit of concurrent rendering.
- patterns/application-state-layer-outside-react — Zalando's alternative to hook-based data loading.
- systems/react — the runtime.