React hydration errors and what they mean for SEO
If your browser console on a React or Next.js site shows "Minified React error #418," the page has a hydration error. It rarely stops Google from indexing the content, so it's easy to leave alone. It still costs you layout stability, responsiveness, and trustworthy analytics, and it usually points at a markup fault worth fixing at the source.
What hydration is
A server rendered React page arrives as finished HTML, so visitors and crawlers see content immediately. Then React loads in the browser and "attaches" itself to that HTML, wiring up buttons, menus, and forms. React calls this hydration: it turns the server's HTML snapshot into an interactive app.
For that to work, the HTML React expects to find has to match what the server sent. React's documentation says it expects the rendered content to be identical, and that you should treat mismatches as bugs and fix them.
What error #418 means
Production builds of React strip long error messages to save bytes and print a number instead. React's error page decodes #418 as: hydration failed because the server rendered HTML didn't match the client, and as a result the tree will be regenerated on the client. In plain terms, React gave up on the server's HTML for that part of the page and built it again in the browser.
What it means for search
Google crawls the server's response first, and server rendering means the content is already in that HTML. Google's JavaScript SEO documentation still recommends server side or pre-rendering for exactly that reason: it's faster for users and crawlers, and not every bot runs JavaScript. A hydration error happens after that HTML is delivered, so the words on the page usually still reach the index.
The costs show up elsewhere:
- Layout shift. When React throws away server markup and re-renders, elements can jump, which counts against Cumulative Layout Shift.
- Slower interaction. Rebuilding part of the page on the main thread can delay the response to the visitor's first tap or click, which shows up in Interaction to Next Paint.
- Broken widgets. Components in the rebuilt section lose whatever state they had, so a menu, carousel, or form can reset or stop responding.
- Messy analytics. Tags that fire on load can fire twice, or against elements that have since been replaced.
Head tags are a separate concern. In Next.js, metadata set with generateMetadata or a metadata export is resolved on the server and sent as HTML, so titles and canonicals don't depend on hydration succeeding.
The usual causes
React's own list covers most of what I find on real sites:
- Invalid nesting. A
divinside ap, or a link inside a link. The browser's HTML parser rearranges invalid nesting as it reads the page, so the DOM no longer matches the tree React rendered on the server. - Unbalanced HTML from a CMS. Rich text injected with
dangerouslySetInnerHTMLthat contains a stray closing tag or an unclosed element. The browser repairs it on the way in, and React finds a different structure from the one it expected. - Dates and locales. A date formatted on the server in one time zone or locale and on the client in another.
- Random values. IDs or keys generated with
Math.random()orDate.now(), which differ between the two renders. - Browser checks during render. A branch like
typeof window !== 'undefined'that renders one thing on the server and another in the browser. - Browser extensions. Extensions that edit the page before React loads. These show up only for some visitors and aren't yours to fix, which is why it's worth reproducing in a clean browser profile first.
Finding them across a whole site
The browser console shows you one page at a time, and hydration errors often live on a handful of templates or on pages whose CMS content happens to contain bad markup. Spot checking misses them. I script headless Chrome with Playwright to load every URL in the sitemap or the crawl, collect each console error with the URL it came from, and group the results by template. A few hundred pages run in minutes.
The production build tells you only that a mismatch happened. To see where, run the same page against a development build, where React warns about each mismatch with its full message instead of a number. Once the list is grouped, the cause for each template is usually obvious within a few minutes of reading.
Fixing them at the source
- Correct the nesting in the component, using a
divorspanwhere a paragraph can't legally contain the child. - Repair the content that carries unbalanced HTML, and sanitize rich text on the way into the CMS or at render time.
- Make server and client agree on dates by fixing the time zone and locale, and generate IDs with React's
useIdrather than random numbers. - Move browser only logic into an effect that runs after hydration.
- Silence only the unavoidable. React offers
suppressHydrationWarningfor a single element whose text must differ, such as a timestamp. Use it on that element and nowhere else.
Then stop the problem coming back. A small check in the build that parses every rendered page and fails on unbalanced tags or invalid nesting catches the CMS content that would have caused the next one.
