Have you ever seen this error in your Next.js app?
Hydration failed because the server-rendered HTML did not match the client....
What is Hydration ?
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,
- What is Hydration in Next.js?
- What causes hydration errors in Next.js ?
- How to debug and fix ?
- Best practices to avoid.
What is Hydration in Next.js?
Hydration is the process where react attaches interactivity to a pre-rendered HTML page.
- Server-side rendering (SSR): The server sends fully rendered HTML to the browser.
- Client-side hydration: React reuses this HTML but adds event listeners and dynamic content.
In short, If what the server rendered doesn’t match what the client renders, you get a hydration error.
Common Causes of Hydration Errors & Fixes
1. Mismatched Content Between Server & Client
Problem: If the server and client render different content, React will throw a hydration error. Example:
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()
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.
2. Using window, document, or localStorage in SSR
Problem: Server-side rendering (SSR) happens before the browser loads, so window, document, and localStorage don’t exist.
Example (Incorrect Usage):
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.
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.
3. Using Random or Time-Based Values in Server Components
Problem: Values like Math.random() or Date.now() generate different results on the server and client, causing a mismatch.
Example (Incorrect Usage):
export default function Home() {
return <h1>Random Number: {Math.random()}</h1>;
}
✅ Fix: Generate the value only on the client side using useState & useEffect.
import { useState, useEffect } from "react";
export default function Home() {
const [random, setRandom] = useState(null);
useEffect(() => {
setRandom(Math.random());
}, []);
return <h1>Random Number: {random}</h1>;
}
4. Conditional Rendering Based on Client Data
Problem: If a component renders differently on the server and client, React will throw a hydration error.
Example:
export default function Home() {
return <h1>{navigator.userAgent}</h1>;
}
❌ Error: navigator is only available on the client
✅ Fix: Use useEffect() to update after hydration
import { useState, useEffect } from "react";
export default function Home() {
const [userAgent, setUserAgent] = useState("");
useEffect(() => {
setUserAgent(navigator.userAgent);
}, []);
return <h1>{userAgent}</h1>;
}
5. Third-Party Scripts Running on the Server
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 }
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.
Best Practices to Avoid Hydration Errors
- Always use useEffect() for client-specific code (e.g., window, document, localStorage)
- Use placeholders or default values for SSR-rendered content
- Wrap third-party libraries with dynamic() and ssr: false
- Avoid using random values (Math.random(), Date.now()) directly in server-rendered components
- Use useState() for dynamic values and update them in useEffect()
Conclusion
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.
Top comments (2)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.