React5 min read

React Hydration Error: Text Content Does Not Match

Getting hydration errors in Next.js or React? Here's why it happens and how to fix it without breaking your app.

Sarah Chen
December 19, 2025
8.9k234

Hydration errors occur when server-rendered HTML doesn't match what React renders on the client. This happens in Next.js and other SSR frameworks.

**Error Message:** ``` Warning: Text content did not match. Server: "December 19" Client: "December 20"

Hydration failed because the initial UI does not match what was rendered on the server ```

Root Cause

The server renders your component at one time. The client hydrates at a different time. If they produce different output, React throws a hydration error.

Common Causes

### 1. Date and Time Rendering ```javascript function Component() { return <div>{new Date().toString()}</div> // Different every render } ```

### 2. Random Values ```javascript function Component() { return <div>{Math.random()}</div> // Different every time } ```

### 3. Browser-Only APIs ```javascript function Component() { return <div>{window.innerWidth}</div> // window doesn't exist on server } ```

### 4. localStorage Access ```javascript function Component() { const theme = localStorage.getItem('theme'); return <div className={theme}>...</div> // localStorage is client-only } ```

Solution 1: useEffect for Client-Only Code

Move dynamic content to useEffect:

```javascript function Component() { const [time, setTime] = useState(''); useEffect(() => { setTime(new Date().toString()); }, []); return <div>{time}</div>; } ```

Server renders empty string. Client renders empty string, then updates. No mismatch.

Solution 2: Check Window Existence

```javascript function Component() { const width = typeof window !== 'undefined' ? window.innerWidth : 1200; return <div>Width: {width}</div>; } ```

Both server and client render the same initial value.

Solution 3: Suppress Hydration Warning

For intentional mismatches like timestamps:

```javascript <div suppressHydrationWarning> {new Date().toString()} </div> ```

Use sparingly. Only when you know the mismatch is acceptable.

Solution 4: Dynamic Import (Next.js)

Disable SSR for specific components:

```javascript import dynamic from 'next/dynamic';

const ClientComponent = dynamic( () => import('./ClientOnly'), { ssr: false } );

function Page() { return <ClientComponent />; } ```

Component only renders on client.

Practical Example

**Problem:** Showing "Last updated X minutes ago"

```javascript // This breaks function UpdateTime({ timestamp }) { return <span>{getTimeSince(timestamp)}</span>; } ```

Server calculates "2 minutes ago". User visits later, client calculates "62 minutes ago". Mismatch.

**Solution:**

```javascript function UpdateTime({ timestamp }) { const [timeAgo, setTimeAgo] = useState('');

useEffect(() => { setTimeAgo(getTimeSince(timestamp)); const interval = setInterval(() => { setTimeAgo(getTimeSince(timestamp)); }, 60000);

return () => clearInterval(interval); }, [timestamp]);

return <span>{timeAgo}</span>; } ```

Debugging Tips

1. Check browser console for the exact mismatch 2. Look for Date, Math.random, or window usage 3. Search for localStorage or sessionStorage 4. Verify all server/client code paths are identical

Prevention Checklist

- Use useEffect for time-dependent values - Check `typeof window !== 'undefined'` before using browser APIs - Load localStorage values in useEffect - Use suppressHydrationWarning only when necessary - Consider dynamic imports for client-only components

Key Takeaway

If a value changes between server and client render, move it to useEffect or use proper guards. Hydration errors indicate a real problem in SSR logic.

#React#Next.js#Hydration#SSR