The Error
You open your Next.js app and see something like:
Error: Hydration failed because the server rendered HTML didn't match the client.
As a result this tree will be regenerated on the client.or in older versions:
Text content does not match server-rendered HTML.Hydration is when React takes the HTML the server sent and attaches event handlers to it in the browser. To do that, React renders your components again in the browser and expects the exact same output. If anything differs, you get a hydration error.
๐ฏ Quick Fix
Find what renders differently on the server and the client:
- Dates, times, random numbers โ render them only after mount, or pass a fixed value from the server.
window,localStorage,navigatorโ read them inuseEffect, not during render.- Invalid HTML nesting (
<div>inside<p>) โ fix the markup. - Browser extensions โ test in an incognito window.
Why It Happens
10:30:01
B->>B: React rendersThe Common Causes and Fixes
1. Dates and Times
// โ different on server and client
{new Date().toLocaleTimeString()}
// โ
render after mount
"use client";
import { useEffect, useState } from "react";
export function Clock() {
const [time, setTime] = useState(null);
useEffect(() => setTime(new Date().toLocaleTimeString()), []);
return {time ?? "โ"}
;
} Time zones cause this too: the server may run in UTC while the user is in IST. Format dates with an explicit time zone, or format them on the client.
2. Random Values and IDs
Math.random() or crypto.randomUUID() during render gives different values each time. For element IDs, use React's useId(), which is stable between server and client.
3. Browser-Only APIs During Render
// โ window doesn't exist on the server
const isMobile = window.innerWidth < 768;// โ
read it after mount
const [isMobile, setIsMobile] = useState(false);
useEffect(() => setIsMobile(window.innerWidth < 768), []);The same applies to localStorage (theme, cart), navigator and document.
4. Invalid HTML Nesting
Browsers "fix" invalid HTML before React sees it, so the structure changes:
| โ Invalid | โ Fix |
|---|---|
<p><div>โฆ</div></p> |
Use <div> for the outer element |
<a> inside <a> |
Restructure links |
<table> without <tbody> |
Add <tbody> |
<ul> containing a <div> directly |
Wrap items in <li> |
5. Browser Extensions
Extensions like Grammarly, password managers and translators inject attributes or elements into the page before React hydrates. Test in an incognito window with extensions off. If the error disappears, it isn't your code. For attribute changes on <html> or <body>, you can add suppressHydrationWarning to those tags.
6. Theme or Auth Differences
Reading a dark-mode preference from localStorage during render shows "light" on the server and "dark" on the client. Use a cookie the server can read, or a library like next-themes that handles this.
7. Conditional Rendering on typeof window
// โ renders different trees on server and client
{typeof window !== "undefined" ? : }Use the useEffect mount pattern, or load the component client-only:
import dynamic from "next/dynamic";
const Map = dynamic(() => import("./Map"), { ssr: false }); // in a Client Component8. Data That Changes Between Requests
If the server fetches data and the client fetches it again, results may differ. Pass server-fetched data down as props instead of refetching during the first client render.
Using suppressHydrationWarning Correctly
suppressHydrationWarning only silences a mismatch on that one element's own text or attributes, one level deep. It's fine for unavoidable cases like a timestamp, but using it to hide real bugs leaves broken UI behind.
Debugging Checklist
- Read the diff React prints in the dev overlay: it shows the mismatched text or element
- Test in incognito (extensions off)
- Search for
Date,Math.random,window,localStoragein the component - Validate HTML nesting
- Check time zone differences between server and browser
- Move browser-only logic into
useEffector a client-only dynamic import
Frequently Asked Questions
What is a hydration error in Next.js?
It happens when the HTML rendered on the server differs from what React renders in the browser during hydration. React expects identical output so it can attach event handlers to existing HTML. Common causes are dates, random values, browser-only APIs and invalid HTML nesting.
Can browser extensions cause hydration errors?
Yes. Extensions such as Grammarly, translators and password managers modify the page before React hydrates. Test in an incognito window with extensions disabled. If the error disappears, the extension caused it rather than your code.
How do I disable SSR for one component in Next.js?
Use next/dynamic with ssr set to false inside a Client Component, for example dynamic(() => import('./Map'), { ssr: false }). The component then renders only in the browser, which avoids mismatches for code that depends on browser APIs.
Conclusion
A Next.js hydration error means the server and browser rendered different output. Find the source (dates, random values, browser APIs, invalid HTML, extensions or theme logic) and make the first client render match the server, usually by moving browser-only code into useEffect or rendering it client-only.
Sources
- Next.js docs: Text content does not match server-rendered HTML
- React docs: hydrateRoot and hydration mismatches
- React docs: useId
Last verified: 2 November 2026.
Related reads: Fix export metadata in a Client Component ยท How to Use Environment Variables in Next.js ยท How to Deploy Next.js on Vercel
I write practical guides for developers. Follow on LinkedIn or X, or invite me to speak.