Explain what React hydration is
TL;DR
React hydration is the process of attaching event listeners and making a server-rendered HTML page interactive on the client side. When a React application is server-side rendered, the HTML is sent to the client, and React takes over to make it dynamic by attaching event handlers and initializing state. This process is called hydration.
What is React hydration?
Server-side rendering (SSR)
Server-side rendering (SSR) is a technique where the HTML of a web page is generated on the server and sent to the client. This allows for faster initial page loads and better SEO since the content is already available when the page is loaded.
Hydration process
Hydration is the process that happens after the server-side rendered HTML is sent to the client. React takes the static HTML and "hydrates" it by attaching event listeners and initializing the state, making the page interactive. This process involves:
- Reusing the existing HTML: React uses the HTML generated by the server and does not re-render it from scratch.
- Attaching event listeners: React attaches the necessary event listeners to the existing HTML elements.
- Initializing state: React initializes the component state and props to make the page dynamic.
Example
Here's a simple example to illustrate the concept:
-
Server-side rendering: The server generates the following HTML:
<div id="root"><button>Click me</button></div> -
Client-side hydration: When the HTML is sent to the client, React hydrates it with the following code:
import { hydrateRoot } from 'react-dom/client';function App() {const handleClick = () => {alert('Button clicked!');};return <button onClick={handleClick}>Click me</button>;}hydrateRoot(document.getElementById('root'), <App />);
In this example, the server sends the static HTML with a button to the client. React then hydrates the button by attaching the onClick event listener, making it interactive. (Note: ReactDOM.hydrate from react-dom was removed in React 18 — always use hydrateRoot from react-dom/client.)
Selective and streaming hydration (React 18+)
React 18 introduced selective hydration powered by Suspense. With streaming SSR (renderToPipeableStream / renderToReadableStream), the server can flush HTML in chunks as data becomes ready, and the client can hydrate parts of the tree independently — a slow Suspense boundary no longer blocks the rest of the page from becoming interactive. React also prioritizes hydrating the part of the tree the user is currently interacting with.
import { Suspense } from 'react';function Page() {return (<><Header /><Suspense fallback={<Skeleton />}><Comments /></Suspense><Footer /></>);}
Stable IDs across server and client
Generating IDs (e.g. for aria-labelledby or form htmlFor) with Math.random() or counters causes hydration mismatches because the server and client produce different values. Use the useId hook to get a stable, deterministic ID that matches on both sides.
import { useId } from 'react';function Field() {const id = useId();return (<><label htmlFor={id}>Name</label><input id={id} /></>);}
Suppressing intentional mismatches
If a value is genuinely expected to differ between server and client (e.g. a timestamp or a value derived from window), add suppressHydrationWarning to silence the warning for that single element. Use it sparingly — it does not fix the mismatch, it only hides the warning.
<time suppressHydrationWarning>{new Date().toISOString()}</time>
Benefits of hydration
- Faster initial load: Since the HTML is already available, the initial page load is faster.
- SEO benefits: Search engines can crawl the server-rendered HTML, improving SEO.
- Improved user experience: Users can see the content immediately, even before React has fully taken over.
Challenges of hydration
- Mismatch issues: If the server-rendered HTML does not match the client-side React components, React logs an error. React 19 improved hydration error messages by showing a precise diff of the mismatching attributes/text instead of the previous vague "text content did not match" warning, making them much easier to debug.
- Performance overhead: Hydration can be resource-intensive on large pages. Selective hydration and breaking the tree into
Suspenseboundaries help spread the cost.