Introduction
Hydration mismatch errors are common in React and even more common in Next.js, where server-side rendering (SSR) and client-side rendering must match perfectly.
You may have seen this error:
Warning: Text content does not match server-rendered HTML.
or
Hydration failed because the initial UI does not match what was rendered on the server.
This error means the HTML generated on the server differs from the HTML React generates in the browser.
In this article, we explain hydration mismatch errors in simple terms and show you, step by step, how to fix them in React and Next.js applications.
1. What Is Hydration in React and Next.js?
When using SSR (Server-Side Rendering), React sends pre-rendered HTML from the server.
Then the browser runs JavaScript to attach event listeners and activate the UI — this process is called hydration.
Hydration mismatch happens when:
Server-rendered HTML ≠ Client-rendered HTML
React cannot match DOM nodes
Values are different between the server and the client
This leads to warnings or even UI breaking in Next.js.
2. Common Causes of Hydration Mismatch
Hydration mismatch usually happens because the server and client render different UI.
Most common reasons:
Using browser-only APIs (window, document) during initial render
Rendering different values on the server and client
Using random values during render (like Math.random())
Time-based UI differences (new Date())
Conditional rendering that works only on the client
Fetching data differently on server and client
Race conditions with async components
Understanding the cause is the first step to fixing the issue.
3. Fix 1: Prevent Browser-Only Code from Running on the Server
React and Next.js SSR runs on the server where window and document do not exist.
❌ Wrong
const width = window.innerWidth;
✔ Fix using useEffect
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
Why It Works
4. Fix 2: Avoid Time-Based or Random Values During Render
❌ Wrong
<p>{new Date().toISOString()}</p>
❌ Wrong
<p>{Math.random()}</p>
These values differ between server and client.
✔ Fix
const [time, setTime] = useState(null);
useEffect(() => {
setTime(new Date().toISOString());
}, []);
5. Fix 3: Wrap Client-Only Components in "use client" (Next.js 13+)
If you are using Next.js 13 with the App Router:
Example
"use client";
export default function ClientOnlyComponent() {
return <div>Client side only</div>;
}
Why?
6. Fix 4: Use Dynamic Import with SSR Disabled
If a component depends on browser APIs or behaves differently on the client, disable SSR.
Example
import dynamic from "next/dynamic";
const MapComponent = dynamic(() => import("../components/MapComponent"), {
ssr: false,
});
export default function Page() {
return <MapComponent />;
}
Why It Works
7. Fix 5: Ensure Initial State Matches on Server and Client
❌ Wrong (Server: 0, Client: 10)
const [count] = useState(window.innerWidth);
✔ Fix
const [count, setCount] = useState(0);
useEffect(() => {
setCount(window.innerWidth);
}, []);
Initial state must match in SSR.
8. Fix 6: Avoid Conditional Rendering That Changes Between Server and Client
❌ Wrong
{typeof window !== "undefined" && <Sidebar />}
This causes SSR to render nothing, and client to render <Sidebar /> — mismatch.
✔ Fix using dynamic import
const Sidebar = dynamic(() => import("./Sidebar"), { ssr: false });
9. Fix 7: Use useEffect Instead of useLayoutEffect on the Server
React warns when using useLayoutEffect during SSR.
✔ Fix
useEffect(() => {
// safe for client
}, []);
If you must use it, wrap it:
const useIsomorphicLayoutEffect =
typeof window !== "undefined" ? useLayoutEffect : useEffect;
10. Fix 8: Ensure Data Fetching Uses the Same Source
Next.js App Router allows server components and client components.
If the server fetches data differently from the client, HTML will differ.
Fix
Use getServerSideProps (Pages Router)
Use Server Components for fetching in App Router
Pass data as props to client components
11. Fix 9: Use Suspense and Streaming Properly (Next.js 13+)
Streaming can cause UI mismatches if not coordinated.
Tips
12. Debugging Hydration Errors (Important)
1. Check Browser Console
It shows exact element mismatch.
2. Add temporary logs
console.log("Server HTML:", htmlString);
console.log("Client Render", element);
3. Disable JavaScript temporarily
If server UI looks different → mismatch exists.
4. Use React DevTools Hydration Debug Mode (React 18+)
Helps locate mismatched components.
13. Best Practices to Avoid Hydration Mismatches
Keep server and client initial state identical
Avoid browser-only code in SSR
Avoid random or time-based values during render
Use useEffect for client-only logic
Prefer dynamic(..., { ssr: false }) for browser components
Use Server Components for fetching data
Test with JavaScript disabled to verify SSR output
Conclusion
Hydration mismatch errors in React and Next.js happen when the HTML rendered on the server does not match what React renders in the browser. By avoiding browser-only code during SSR, ensuring consistent state, handling time-based and random values correctly, and using Next.js features like dynamic imports and client components, you can completely eliminate hydration issues. With these best practices, your React and Next.js apps will become more stable, error-free, and production-ready.