TL;DR / Selective hydration lets React prioritize hydrating the components a user is actively interacting with, rather than hydrating the entire page in a fixed order.
How It Works
Page with Suspense boundaries:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Nav │ │ Sidebar │ │ Feed │
│ (hydrated) │ │ (pending) │ │ (pending) │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────────────┐ ┌──────────────┐
│ User clicks Feed │────────┐ │ Feed │
└──────────────────┘ └────────→│ (priority ^) │
└──────────────┘
React hydrates Feed first,
replays the click event,
then hydrates Sidebar.
Selective hydration is a React 18+ feature that combines streaming SSR with concurrent rendering to intelligently order hydration work based on user behavior. It solves a fundamental problem: when the user clicks a button before hydration completes, traditional hydration either ignores the click or forces the entire page to hydrate synchronously before responding.
The Problem It Solves
Without selective hydration, React hydrates the page in tree order — top to bottom, left to right. If the user taps a button in a component at the bottom of the page, React must first hydrate every component above it. On a complex page, this can take hundreds of milliseconds during which the UI appears frozen.
The user sees a fully rendered page (thanks to SSR), tries to interact with it, and gets no response. This is the "uncanny valley" of server rendering — the page looks ready but is not. Selective hydration closes this gap by making hydration responsive to user intent.
How It Works
Selective hydration requires two ingredients: Suspense boundaries and React's concurrent renderer. Each Suspense boundary defines an independently hydratable unit. When the page loads, React begins hydrating these units based on their default priority. But when a user interacts with a not-yet-hydrated section, React performs the following sequence:
- Interrupts the current hydration work (possible because concurrent rendering makes hydration interruptible).
- Reprioritizes the section the user interacted with to the highest hydration priority.
- Hydrates that section synchronously.
- Replays the user's event after hydration completes, so the click or keystroke is not lost.
- Resumes hydrating the remaining sections in their original order.
This event replay mechanism is critical. React captures discrete events (clicks, key presses) that target not-yet-hydrated DOM during the SSR HTML phase. Once the relevant section hydrates, React dispatches those captured events against the now-interactive component tree. The user's action is never lost — it is just deferred by a few milliseconds.
Suspense as the Hydration Boundary
Each <Suspense> boundary becomes a hydration unit. Without Suspense, the entire app is one hydration unit and cannot be selectively hydrated. The granularity of your Suspense boundaries directly controls the granularity of selective hydration.
Consider a page with a nav, sidebar, main content, and comments section, each wrapped in Suspense. If the user clicks a reply button in the comments section while the sidebar is still hydrating, React will pause sidebar hydration, hydrate the comments section, process the click, and then return to the sidebar.
The Streaming Connection
Selective hydration pairs naturally with streaming SSR. As the server streams HTML chunks for each Suspense boundary, the client can begin hydrating earlier chunks while later chunks are still arriving. If the user interacts with a section whose HTML has arrived but whose JavaScript is still loading, React cannot hydrate it yet — but it records the event and hydrates that section the moment its code arrives.
This creates a pipeline: server streams HTML, browser paints it, JavaScript code-splits arrive progressively, and React hydrates each section prioritized by user interaction. The perceived interactivity gap shrinks dramatically compared to monolithic hydration.
Requirements and Limitations
Selective hydration only works with hydrateRoot (React 18+) and the concurrent renderer. It requires Suspense boundaries around the sections you want to be independently hydratable. It also requires that the HTML was server-rendered — client-only rendering has nothing to selectively hydrate.
The feature is automatic once the prerequisites are met. You do not call any special API. React's internal scheduler handles the prioritization based on user events. The concurrent rendering lanes system assigns hydration work a default lane and promotes it when interaction is detected.
Gotchas
- Only discrete events trigger reprioritization. Clicks, key presses, and similar discrete events cause React to prioritize hydration. Continuous events like
mousemoveorscrolldo not — they would cause too much priority thrashing. - Without Suspense boundaries, selective hydration does nothing. The entire app is one unit and must hydrate completely. Add Suspense boundaries around independently interactive sections.
- Event replay only works for React-managed events. Native event listeners added via
addEventListenerorrefcallbacks are not replayed. Only events that go through React's synthetic event system are captured and replayed. - Hydration order is non-deterministic from the user's perspective. If you rely on hydration side effects (like analytics tracking), they may fire in different orders depending on user behavior.
- Code splitting is essential. Selective hydration deprioritizes unneeded sections, but if all component code is in one bundle, the browser still downloads everything upfront. Pair with
React.lazy()for maximum benefit.