Skip to content

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, replacing renderToNodeStream / renderToString.
  • hydrateRoot — new client hydration entry point, replacing hydrate. Does per-boundary hydration, supports onRecoverableError to 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 on hydrateRoot for 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 — SuspenseList experimental, useTransition not 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-reactcanonical 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-18 useContext-change-across-ready-boundary bug (fixed in 19) and the required state-injection-ordering discipline for hydration.
Last updated · 501 distilled / 1,218 read