In a server-rendered React app, what is a hydration mismatch, and what does React 19 do when it detects one?
answer
- server and client disagreed on first render
- the same components ran twice
- time, randomness, locale, browser-only reads
- React discards, it does not patch
- falls back to client rendering that subtree
basics
~20 sA hydration mismatch means the markup React produced on the server differs from what the same components produce on the client's first render. React 19 logs a hydration error and re-renders the affected subtree on the client, throwing away that server HTML.
solid answer
~50 sHydration is React re-running your components in the browser over HTML the server already produced, adopting those existing DOM nodes and attaching state and event listeners instead of creating new ones. That only works if the client's first render describes exactly the same tree: same elements in the same order, same text, same attributes. When it doesn't, React reports a hydration error — in React 19 the console message includes a diff showing which server value and which client value disagreed. React does not try to patch the difference node by node. It discards the server HTML for the nearest Suspense boundary, or the whole root if there is none, and client-renders that subtree from scratch. So the practical cost is real: you paid for server rendering, then threw part of it away, and the user may see content shift as the client version replaces it.
go deeper
Be able to say plainly that server HTML and the client's first render disagreed, and name two causes — a random or time-based value, and code that reads window or localStorage while rendering.
Explain the mechanism: hydration adopts existing DOM rather than creating it, so it requires an identical tree, and React recovers by client-rendering the affected subtree instead of patching individual nodes.
Show the debugging routine — ask what the server could not know, read the diff React prints, and reach for a deterministic prop or a post-mount effect. Talk about the cost: lost server work, flashes, layout shift.
Own the prevention strategy. Decide where Suspense boundaries sit so a mismatch's blast radius is bounded, set a team rule that render must be a pure function of props and server-known data, and treat hydration errors as build- or monitoring-visible rather than console noise.
## What hydration is asserting Server-side rendering runs your components on the server and serializes the result to an HTML string. The browser parses that HTML and paints it, so the user sees content before any JavaScript has run. Then the React bundle loads and *hydration* begins: React renders the same component tree in the browser, but instead of creating DOM nodes it walks the nodes that are already there, claims them, and wires up state and event handlers. Hydration is therefore an assertion, not a merge. React is saying: *the tree I am producing right now is the same tree that produced this HTML.* A hydration mismatch is that assertion failing. ## What counts as a mismatch Anything React can compare while walking the existing DOM: - different text content in a node, - a different element type at a given position (`<span>` where the HTML has `<div>`), - an extra or missing child, or children in a different order, - differing attributes on an element. ## Why the same components produce two different trees The component code is identical, so something outside the code differs between the two runs: - **Nondeterministic values.** `Date.now()`, `new Date()`, `Math.random()`, a module-level counter used to build ids. The server run and the client run simply produce different numbers. - **Environment differences.** The server usually runs in UTC with a server-side locale; the browser has the user's timezone and locale. Any `toLocaleString`/`Intl` formatting can differ even when the underlying value is identical. - **Browser-only reads.** `window`, `document`, `localStorage`, `matchMedia`, `navigator.userAgent`. These do not exist on the server, so code that branches on them (`typeof window !== 'undefined' ? a : b`) renders one thing on the server and the other on the client — a self-inflicted mismatch. - **Invalid HTML nesting.** The browser's parser silently restructures illegal markup, so the DOM React finds is not the DOM the server string described. - **Outside mutation.** Browser extensions or third-party scripts that inject or rewrite nodes before hydration. ## What React 19 does about it React logs a hydration error to the console. React 19 improved this message specifically: it prints a diff of the offending subtree marking which parts came from the server and which from the client, instead of the older "text content did not match" one-liner. Then it recovers by *client rendering*. React does not patch individual text nodes to make them agree; it drops the server-rendered DOM for the smallest enclosing Suspense boundary — or the entire root when the mismatch is not inside one — and renders that subtree fresh on the client. The application ends up correct, which is why a mismatch is a console error rather than a blank page, but you lost the server work for that region and the user can see a visible replacement or layout shift. That recovery behaviour is also why Suspense boundary placement matters for blast radius: a mismatch deep inside a boundary costs you that boundary, not the page. ## Fixing one, in three shapes **Make the render deterministic.** Compute the varying value once, on the server, and pass it down as a prop — a timestamp as an ISO string, a locale from the request — so both runs read the same input. This is the best fix when it is available. **Render the neutral value, correct after mount.** Render whatever the server can know, then update in an effect, which runs only in the browser after hydration has succeeded: ```jsx function Timestamp({ iso }) { const [text, setText] = useState(iso); // both renders agree here useEffect(() => { setText(new Date(iso).toLocaleString()); // browser-only, after hydration }, [iso]); return <time dateTime={iso}>{text}</time>; } ``` This costs one extra client render and a brief flash of the server value, and it is correct by construction. **Tell React you expect the difference.** `suppressHydrationWarning` on the element silences the report for that element's own text and attributes. It is an escape hatch for genuinely unavoidable differences, not a fix. ## The diagnosis habit When you hit one, ask a single question: *what did the server not know?* The answer is nearly always time, timezone, locale, randomness, or something only the browser can read. That short list is why interviewers like this question — a candidate who has actually debugged SSR names the categories immediately instead of guessing.
- How much of the page does React throw away when hydration fails?Not the whole app by default — React client-renders the nearest enclosing Suspense boundary. If the mismatch happens outside any boundary, the fallback is the entire root. That makes boundary placement a blast-radius decision: wrapping a risky, client-dependent region in its own Suspense boundary keeps a mismatch there from costing you the rest of the server-rendered page.
- Why not just let React patch the difference instead of re-rendering the subtree?Because React cannot tell whether the difference is cosmetic or structural. If the trees diverge, the DOM nodes React would adopt may not correspond to the elements it thinks it is adopting, so state, refs and listeners would attach to the wrong nodes. Re-rendering the subtree from the client's own description guarantees consistency; patching would guarantee only that the text looks right.
- Is a hydration mismatch ever harmless enough to ignore?Rarely. Even the cosmetic-looking ones cost you the server rendering for that subtree and can cause a visible flash or layout shift, and mismatches often travel in pairs with a real bug — a component reading browser state during render. Treat the error as actionable and fix the source; reach for suppressHydrationWarning only for a difference you genuinely intend, like a rendered local timestamp.
saying these in an interview costs you the question
- Says React silently patches the text to match the client
- Claims mismatches only happen in development builds
- Blames React's diffing algorithm rather than nondeterministic render input
- Thinks the fix is to disable server rendering for the whole page
- Says the warning is cosmetic and can be ignored in production