The "Hydration failed" error occurs in React apps with Server-Side Rendering (SSR) "” like Next.js "” when the HTML generated on the server doesn't match what React tries to render on the client. React "hydrates" the server HTML by attaching event listeners, but if the content differs, it throws this error.
// ❌ Problem "” different output on server vs client
function Clock() {
return <p>Time: {new Date().toLocaleTimeString()}</p>; // Different each render!
}
// ✅ Solution "” use useEffect for client-only content
function Clock() {
const [time, setTime] = useState('');
useEffect(() => {
setTime(new Date().toLocaleTimeString()); // Only runs on client
}, []);
return <p>Time: {time || 'Loading...'}</p>;
}
// ❌ window doesn't exist on server
function ThemeToggle() {
const theme = localStorage.getItem('theme') || 'light'; // Error on server!
return <div className={theme}>Content</div>;
}
// ✅ Read localStorage only on client via useEffect
function ThemeToggle() {
const [theme, setTheme] = useState('light'); // Default for SSR
useEffect(() => {
const saved = localStorage.getItem('theme');
if (saved) setTheme(saved); // Only runs on client
}, []);
return <div className={theme}>Content</div>;
}
// ❌ Invalid HTML "” div inside p
<p>
<div>This is invalid HTML!</div> {/* Browser auto-corrects, causing mismatch */}
</p>
// ❌ a inside a
<a href="/outer">
<a href="/inner">Nested links</a> {/* Invalid! */}
</a>
// ✅ Use span inside p (inline elements only)
<p>
<span>This is valid!</span>
</p>
// ✅ Or change p to div
<div>
<div>This is valid!</div>
</div>
// ✅ suppressHydrationWarning for intentional differences
<time suppressHydrationWarning>
{new Date().toLocaleTimeString()}
</time>
// ✅ Dynamic import with ssr: false (Next.js)
import dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(
() => import('./ClientOnlyComponent'),
{ ssr: false } // Don't render on server
);
// ✅ Render client-only content after mount
function ClientOnly({ children }) {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return null; // Same as server output
return children;
}
// Usage
<ClientOnly>
<ComponentThatUsesWindow />
</ClientOnly>
Hydration is the process where React takes server-rendered HTML and attaches JavaScript event listeners to make it interactive. If the server HTML doesn't match what React expects to render, hydration fails.
localStorage doesn't exist on the server (Node.js). If you read it during render, the server renders one thing and the client renders another (with the stored value), causing a mismatch.
Access window inside useEffect (which only runs on the client), check typeof window !== "undefined" before using it, or use dynamic imports with ssr: false.
It's a React prop that tells React to ignore hydration mismatches for that element. Use it sparingly for intentional differences like timestamps. It doesn't fix the underlying issue.
No. Hydration errors only occur in SSR frameworks like Next.js, Remix, or Gatsby. Standard Create React App or Vite apps don't use SSR by default, so they won't have this error.
Explore 500+ free tutorials across 20+ languages and frameworks.