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.
// Wrong Problem "" different output on server vs client
function Clock() {
return <p>Time: {new Date().toLocaleTimeString()}</p>; // Different each render!
}
// Correct 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>;
}
// Wrong window doesn't exist on server
function ThemeToggle() {
const theme = localStorage.getItem('theme') || 'light'; // Error on server!
return <div className={theme}>Content</div>;
}
// Correct 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>;
}
// Wrong Invalid HTML "" div inside p
<p>
<div>This is invalid HTML!</div> {/* Browser auto-corrects, causing mismatch */}
</p>
// Wrong a inside a
<a href="/outer">
<a href="/inner">Nested links</a> {/* Invalid! */}
</a>
// Correct Use span inside p (inline elements only)
<p>
<span>This is valid!</span>
</p>
// Correct Or change p to div
<div>
<div>This is valid!</div>
</div>
// Correct suppressHydrationWarning for intentional differences
<time suppressHydrationWarning>
{new Date().toLocaleTimeString()}
</time>
// Correct Dynamic import with ssr: false (Next.js)
import dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(
() => import('./ClientOnlyComponent'),
{ ssr: false } // Don't render on server
);
// Correct 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>
Try this next
0 of 2 completed
The server-rendered HTML can look fine before React takes over, but hydration compares that HTML with what the client render produces. If the text, attributes, element order, or generated IDs differ, React cannot safely attach event handlers to the existing DOM.
localStorage exists only in the browser. If a component reads it during render, the server must render without that value while the client may render with a saved value immediately.
Do not read window during server render. Put browser-only reads inside useEffect, guard utility code with typeof window !== "undefined", or load a browser-only component dynamically with SSR disabled when the whole component depends on the DOM.
Explore 500+ free tutorials across 20+ languages and frameworks.