React hydration error messages mean React could not reuse the HTML the server sent: the first render in the browser produced different markup, or something failed while React attached to the page. The wording depends on your React version and on whether the build is minified. Find the text you see below and open its page for the fix.
Every message, by React version
React 19 merged most of the React 18 messages into one error with a diff, and production builds replace every message with a numbered code.
| Page | React 18 wording | React 19 wording | Code |
|---|---|---|---|
| Server rendered HTML didn't match | Not in React 18 | Hydration failed because the server rendered HTML didn't match the client. | #418 |
| Initial UI does not match | Hydration failed because the initial UI does not match what was rendered on the server. | Replaced by the row above | #418 |
| Text content does not match | Text content does not match server-rendered HTML. and the warning Text content did not match. Server: … Client: … | Hydration failed because the server rendered text didn't match the client. | #425 in 18, #418 in 19 |
| Attributes didn't match | One warning per attribute (the next two rows) | A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. | None: development only |
| Prop className did not match | Warning: Prop `className` did not match. Server: … Client: … | A line in the attribute diff | None |
| Expected server HTML to contain | Warning: Expected server HTML to contain a matching <div> in <div>. | A + or - element in the diff | #418 follows it |
| Extra attributes from the server | Warning: Extra attributes from the server: class,style | A - line in the attribute diff | None |
| Error while hydrating | There was an error while hydrating. (root) or …this Suspense boundary. | There was an error while hydrating but React was able to recover… | #423, #422 |
| div cannot be a descendant of p | validateDOMNesting(...): <div> cannot appear as a descendant of <p>. | In HTML, <div> cannot be a descendant of <p>. | None: development only |
| validateDOMNesting | validateDOMNesting(...): <tr> cannot appear as a child of <table>. | In HTML, <tr> cannot be a child of <table>. | None: development only |
| Update before hydrating | This Suspense boundary received an update before it finished hydrating. | No longer reported | #421 |
| Missing getServerSnapshot | Missing getServerSnapshot, which is required for server-rendered content. | Same | #407 |
| window is not defined | ReferenceError: window is not defined | Same: a server crash, not a React error | None |
The production codes have their own pages: minified React error #418, #423, #425, and every hydration code in one table.
Which one am I seeing?
Look at how the message starts:
| It starts with | What happened | Go to |
|---|---|---|
Minified React error # | A production build. The text is replaced by a code | Minified React error codes |
Hydration failed because | React threw the server HTML away and rendered it again in the browser | React 19 or React 18 |
Warning: | A React 18 development warning that names the element and both values | The matching row in the table above |
A tree hydrated but | React 19 kept the page, but an attribute differs and stays wrong | Attributes didn't match |
In HTML, or validateDOMNesting | Invalid tag nesting that the browser rewrites before React runs | validateDOMNesting |
There was an error while hydrating | The follow-up to an earlier error. Scroll up to the first one | Error while hydrating |
ReferenceError | The server crashed before any hydration happened | window is not defined |
React prints several of these for one bug. In React 18 a single mismatch
often logs a warning, then Hydration failed… or
Text content does not match…, then There was an error while hydrating…. The first message is the
one that names the element. Fix that one first.
To see which wording you get, check the React version your app uses:
npm ls react react-dom # pnpm why react · yarn why react · bun pm lsNext.js 15 and later run the App Router on React 19. Next.js 13 and 14 print the React 18 wording.
Find the cause, whatever the message
Each message is a symptom. The same few causes produce all of them: the clock,
the user's locale or timezone, window and localStorage read during render,
invalid HTML, CSS-in-JS class names, browser extensions and scripts that edit
the page first. Common causes of hydration errors has a fix
guide for each, and the React hydration error guide
explains the mechanism behind all of them.
To find every mismatch in an app at once, instead of one console message at a time, load every route with hydration-proof:
npx hydration-proof test --mode bothIt reports each difference with a stable issue code, the element, the server and client values, the source line and the likely cause.