Have you ever seen this error in your Next.js app?
Hydration failed because the server-rendered HTML did not match the client....
Hydration is the process where static HTML becomes interactive by adding javascript to it.
When a web page is rendered on the server it loses its interactivity and event handles before getting to the client.
Now react come into the picture. React adds the interactivity and event handlers that were lost when the HTML was server-side rendered.
Lets understand in some simple terms, This is a hydration error, and it happens when the server renders one version of your page, but the browser (client) renders something different.
It’s one of the most common issues in Next.js, especially if you're using dynamic content, third-party scripts, or certain hooks like useEffect.
In this blog, we’ll cover,
Hydration is the process where react attaches interactivity to a pre-rendered HTML page.
In short, If what the server rendered doesn’t match what the client renders, you get a hydration error.
Problem: If the server and client render different content, React will throw a hydration error. Example:
1
2
3
export default function Home() {
return <h1>{new Date().toLocaleTimeString()}</h1>;
}
The server renders a fixed timestamp when it builds the page.
The client re-renders with the current time, causing a mismatch.
✅ Fix: Wrap dynamic content inside useEffect()
1
2
3
4
5
6
7
8
9
10
11
import { useEffect, useState } from "react";
export default function Home() {
const [time, setTime] = useState("");
useEffect(() => {
setTime(new Date().toLocaleTimeString());
}, []);
return <h1>{time}</h1>;
}
✔ Now, the server renders a placeholder (""), and the client updates the time after hydration.
Problem: Server-side rendering (SSR) happens before the browser loads, so window, document, and localStorage don’t exist.
Example (Incorrect Usage):
1
2
3
export default function Home() {
return <h1>Screen Width: {window.innerWidth}</h1>;
}
❌ Error: window is not defined on the server
✅ Fix: Use useEffect() to access window on the client side.
1
2
3
4
5
6
7
8
9
10
11
import { useState, useEffect } from "react";
export default function Home() {
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
return <h1>Screen Width: {width}</h1>;
}
✔ Now, the server renders an empty value (0), and the client updates it after hydration.
Problem: Values like Math.random() or Date.now() generate different results on the server and client, causing a mismatch.
Example (Incorrect Usage):
1
2
3
export default function Home() {
return <h1>Random Number: {Math.random()}</h1>;
}
✔ Fix: Generate the value only on the client side using useState & useEffect.
1
2
3
4
5
6
7
8
9
10
11
import { useState, useEffect } from "react";
export default function Home() {
const [random, setRandom] = useState(null);
useEffect(() => {
setRandom(Math.random());
}, []);
return <h1>Random Number: {random}</h1>;
}
Problem: If a component renders differently on the server and client, React will throw a hydration error.
Example:
1
2
3
export default function Home() {
return <h1>{navigator.userAgent}</h1>;
}
❌ Error: navigator is only available on the client
✅ Fix: Use useEffect() to update after hydration
1
2
3
4
5
6
7
8
9
10
11
import { useState, useEffect } from "react";
export default function Home() {
const [userAgent, setUserAgent] = useState("");
useEffect(() => {
setUserAgent(navigator.userAgent);
}, []);
return <h1>{userAgent}</h1>;
}
Problem: Some third-party libraries (e.g., charts, ads, analytics) try to run on the server but rely on the browser.
✅ Fix: Use dynamic() with { ssr: false }
1
2
3
4
5
6
7
import dynamic from "next/dynamic";
const Chart = dynamic(() => import("chart.js"), { ssr: false });
export default function Home() {
return <Chart />;
}
✔ Now, the component only loads in the browser, preventing hydration issues.
Hydration errors in Next.js happen when the server and client render different content. They can be frustrating, but fixing them is simple:
✅ Use useEffect() for client-only code
✅ Lazy load third-party libraries using dynamic()
✅ Avoid dynamic content in server-rendered components
Following these best practices will help you avoid hydration errors and build a smoother, faster Next.js app.