Hydration failed / text content mismatch means the HTML the server sent doesn't match what the browser rendered on first client-side render.
What "Hydration failed / text content mismatch" Means
This is a React (and Next.js) runtime error that occurs when the server-rendered HTML and the client-side virtual DOM disagree on the initial content. React expects a byte-for-byte match during hydration, and any difference throws this warning or error in the browser console.
Why It Happens
The most common causes are:
- Unstable data sources — using
Date.now(),Math.random(), orlocalStorageduring render produces different values on server vs. client. - Browser-only APIs — accessing
window,document, ornavigatorwithout guards crashes the server render or returns different results. - Conditional rendering based on environment — checking
typeof window !== 'undefined'inside JSX creates divergent output.
Example Code That Triggers It
Here's a minimal Next.js App Router component that will throw this error:
// app/page.tsx
export default function Page() {
return (
<div>
<p>Current time: {new Date().toLocaleTimeString()}</p>
</div>
);
}
The server renders one timestamp. When the client hydrates, new Date() returns a different time. React sees the mismatch and logs: Hydration failed because the server rendered HTML didn't match the client.
How to Fix It
Use useEffect to defer browser-only calculations until after hydration:
// app/page.tsx
'use client';
import { useEffect, useState } from 'react';
export default function Page() {
const [time, setTime] = useState('');
useEffect(() => {
setTime(new Date().toLocaleTimeString());
}, []);
return (
<div>
<p>Current time: {time || 'Loading...'}</p>
</div>
);
}
The fix works because useEffect runs only on the client after hydration completes. Both server and client render the same initial HTML (Loading...), then the client updates it. This guarantees a consistent first paint.
Common Mistakes That Cause This
-
Suppressing the error with
suppressHydrationWarning— this hides the symptom but leaves the underlying bug. You'll get inconsistent UI on first load and no error to trace later. -
Conditional rendering on
typeof window— developers write{typeof window !== 'undefined' && <Component />}thinking it's safe. It's not: the server rendersfalse, the client renders the component, and hydration fails.
When Should You Worry About This?
You should worry when the mismatch affects visible content that users interact with — forms, navigation, or data displays. If it's just a timestamp or a random ID, it's cosmetic but still worth fixing. If it causes layout shift or breaks event listeners, it's critical. React may skip attaching handlers to mismatched nodes, leaving buttons and inputs non-functional.
Also worry if you see this in production logs frequently. It signals your data layer isn't deterministic, which can lead to SEO issues since crawlers may see different content than users.
Next time this error appears, check the first thing that renders dynamic data — Date, Math.random, or any browser API call — and move it into a useEffect or a client-only component boundary.