Skip to content

React Hydration Error Explained in 2 Minutes

Published: Updated:

React rehydration errors (uncaught error: minified React error #423 and #418) happen when the client render doesn't match the server render. This can happen when using SSR or SSG, often done with Gatsby or Next.js.

Here's one example of a component where a rehydration error will happen if you use it as part of build time generation:

1import React from "react"
2
3const RandomNumberGenerator = () => {
4
5 const randomNumber = Math.random();
6
7 return (
8 <p>Random number: {randomNumber}</p>
9 )
10}
11
12export default RandomNumberGenerator

Can you see what will give a hydration error if you use this as a component?

Spoiler: It is the Math.random function. The reason being is that the random number will be first generated on build time, and then again a random number will be generated on client side when React hydrates. These two numbers have almost no chance to be the same number, as it is random.

As the client-side random number doesn't match the random number on server data, React will decide to abandon the server-generated DOM data completely and switch to full client-side render. React pretty much says "server data doesn't look correct, let's abandon it and do everything from scratch". This results in you losing the benefits of build generation data that Gatsby and Next.js provide, and can often cause a flash of content as the page renders again.

This is what the hydration error is all about, a difference between build data and client data. The fix for this is to find the difference between server and client data is, and fix that. You should do this by inspecting console errors on your development build, usually React lets you know what the problem is. With the random number generation, I would make the number only be generated on the client side by using useEffect, that would be one way to solve the issue.

Hydration errors usually only happen on production sites. In local development environment there is no SSR/SSG being used, only client-side rendering, which is why it doesn't happen locally.

Most common causes of hydration error

Table element is missing <thead> and <tbody>

I have found out that if you don't include the <thead> and <tbody> elements on your table, React will give you a hydration error. Those elements are optional, so normally aren't required in HTML, but they are required when doing SSR / SSG with React. The reason for that is that the two elements will be added by browsers to the DOM if they are missing, but client-side rendering doesn't add them to React's shadow DOM. This gives a mismatch of the table data between the static HTML data DOM and client side render, resulting in a hydration error.

Repeating your code won't return the same result

This is the basis of the example code earlier in this article, for example generating a random number can't be replicated. The code has to return the same thing when it is run on build time and on client-side. A fix for this is to either make the code repeatable or make it only run on client-side.

Missing closing tag for an element

If you forget to close an element, it can give a hydration error. A closing tag is for example </div> for a div.

Invalid HTML structure

Invalid HTML structure can give a hydration error. For example this is incorrect HTML: <p><h1>This is a heading</h1></p>. What makes that invalid is the fact that headings can't be placed inside paragraphs.

Another common mistake is accidentally double quoting paragraphs, this is invalid: <p><p>Paragraph</p></p>

You're exporting a variable in an unexpected way

I have found out that exporting a variable (a const in this case) can create a hydration error. I am unsure why, and I can't replicate it on every environment (for example on this blog post it did work just fine, and didn't give errors), but here's the code for it anyway:

This is on a utils file: export const EXAMPLE_ID = 1234567;

Then it is accessed on another file by using this: import { EXAMPLE_ID } from './utils/ids' and <div id={EXAMPLE_ID} />.

This can result in a hydration error. I'm still looking into why it actually happens, but it can happen.

An iframe can give a hydration error? Maybe?

I have heard an iframe give a hydration error, but perhaps it was just a developer error. The fix for the developer was to use useEffect to load the iframe on the client-side instead of having it be part of the build time.

That could have been just a bandaid to the problem though, I don't think a proper iframe can truly give a hydration error. But maybe it can?