How to Fix Hydration Mismatch Errors
Getting 'Hydration failed' or 'Text content mismatch' errors in React, Next.js, or Astro? Here's what causes hydration mismatches and how to fix them.
Your page loads, then breaks. The console says “Hydration failed.” This error means the HTML the server rendered doesn’t match what the client-side JavaScript tried to create. React can’t reconcile the difference, so it either errors out or re-renders everything (losing performance).
AI tools are particularly bad at causing hydration errors because they don’t think about the server vs. client distinction.
What Is Hydration?
In plain English: The server generates HTML. The browser receives it. Then React “hydrates” it — attaching JavaScript interactivity to the existing HTML.
If the HTML React expects to create on the client doesn’t match what the server already sent, you get a hydration mismatch. React can’t “adopt” the server HTML because it’s different from what it would render.
Common Error Messages
# React 18+
Hydration failed because the initial UI does not match what was rendered on the server.
Text content does not match. Server: "X" Client: "Y"
Expected server HTML to contain a matching <div> in <p>.
# Next.js
Error: Hydration failed because the initial UI does not match
Warning: Prop `className` did not match.
# Astro
Hydration directive error or client:load mismatch
5 Common Causes
1. Using Date, Math.random(), or window During Render
These produce different values on server vs. client.
// 💥 This causes a mismatch:
function Greeting() {
const hour = new Date().getHours(); // Different on server vs client!
return <p>It's {hour}:00</p>;
}
// Fix: use useEffect for client-only values
function Greeting() {
const [hour, setHour] = useState<number | null>(null);
useEffect(() => {
setHour(new Date().getHours());
}, []);
return <p>{hour !== null ? `It's ${hour}:00` : 'Loading...'}</p>;
}
2. Invalid HTML Nesting
Browsers auto-correct invalid HTML, creating a mismatch.
// 💥 <p> can't contain <div> — browser auto-fixes it
function Card() {
return (
<p>
<div>This is inside a paragraph</div> {/* Invalid! */}
</p>
);
}
// Fix: use valid nesting
function Card() {
return (
<div>
<div>This is inside a div</div>
</div>
);
}
Common invalid nestings that cause hydration errors:
<p>containing<div>,<p>,<ul>,<table><a>containing<a>(nested links)<button>containing<button>
3. Browser Extensions Modify the DOM
Browser extensions (Dark Reader, Grammarly, ad blockers) inject elements into your HTML, causing mismatches.
// This is NOT your fault — suppress the warning:
// Next.js: add suppressHydrationWarning to <html> or <body>
<html suppressHydrationWarning>
<body suppressHydrationWarning>
{children}
</body>
</html>
4. Conditional Rendering Based on Client-Only APIs
// 💥 window doesn't exist on server
function Layout() {
const isMobile = window.innerWidth < 768; // Server crash or mismatch
return isMobile ? <MobileNav /> : <DesktopNav />;
}
// Fix: detect client-side with useEffect
function Layout() {
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
setIsMobile(window.innerWidth < 768);
const handleResize = () => setIsMobile(window.innerWidth < 768);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return isMobile ? <MobileNav /> : <DesktopNav />;
}
5. localStorage / sessionStorage in Render
// 💥 localStorage doesn't exist on server
function ThemeProvider({ children }) {
const theme = localStorage.getItem('theme') || 'light'; // Server error!
return <div className={theme}>{children}</div>;
}
// Fix: read from storage in useEffect
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
useEffect(() => {
const saved = localStorage.getItem('theme');
if (saved) setTheme(saved);
}, []);
return <div className={theme}>{children}</div>;
}
Step-by-Step Fix Process
Step 1: Read the Error Details
React 18+ tells you exactly what mismatched:
Text content does not match. Server: "Tuesday" Client: "Wednesday"
This tells you: a date or time-dependent value differs between server and client.
Step 2: Find the Dynamic Value
Search your component for anything that varies between server and client:
new Date(),Date.now()Math.random()window.*,document.*,navigator.*localStorage,sessionStorage- Conditional rendering based on client-only state
Step 3: Move Client-Only Logic to useEffect
// The universal fix pattern:
const [clientValue, setClientValue] = useState(serverSafeDefault);
useEffect(() => {
setClientValue(getClientOnlyValue());
}, []);
Step 4: Validate Your HTML
Check for invalid nesting. Use the W3C validator or React DevTools to spot issues.
Framework-Specific Fixes
Next.js (App Router)
// Use 'use client' for components that need browser APIs
'use client';
// Or use dynamic imports to skip SSR entirely
import dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(() => import('./MyComponent'), {
ssr: false,
});
Next.js (Suppress Warnings)
// For things you can't fix (browser extensions, etc.)
<time suppressHydrationWarning>{new Date().toLocaleDateString()}</time>
Astro
<!-- Use client:only to skip SSR for a component -->
<ReactComponent client:only="react" />
<!-- vs client:load which SSRs then hydrates -->
<ReactComponent client:load />
🤖 Prompt to Fix This
I'm getting a hydration mismatch error in my [Next.js/React/Astro] app:
[PASTE THE FULL ERROR MESSAGE]
Here's the component that's causing it:
[PASTE THE COMPONENT CODE]
My setup: [Next.js App Router / Pages Router / Astro with React]
Please:
1. Identify what's causing the server/client mismatch
2. Show the fixed component code
3. Use the right pattern (useEffect, dynamic import, or client:only)
4. Make sure the fix doesn't cause a flash of wrong content
Prevention Tips
- Never access
window,localStorage, ordocumentduring render — always inuseEffect - Use valid HTML nesting — no
<div>inside<p>, no nested<a>tags - Avoid dates/random in render — use
useEffector pass from server as props - Use
suppressHydrationWarningsparingly — only for things you can’t control - Tell AI you’re using SSR — “This is a Next.js App Router server component” changes what AI generates
Related Resources
- 🧙 Debug Wizard — Paste your error for AI-powered fix suggestions
- 📖 Debugging with AI — Systematic debugging workflow
- 📋 Common Errors Cheatsheet — Quick reference for frequent errors