What causes a hydration mismatch in Next.js, and how do you debug and fix it?
Asked by Md Sharifuzzaman Shajib · · 7 answers · Solved
A hydration mismatch in Next.js happens when server-rendered HTML doesn't match the client-rendered version, causing React to throw errors. It’s often due to non-deterministic rendering or client-only code running on the server.
7 answers
Accepted answer · Answered by Md Sharifuzzaman Shajib ·
Using useEffect to Change Content Immediately
❌ Problem:
useEffect(() => {
setValue('Updated');
}, []);
If the server renders 'Initial', but client quickly updates it to 'Updated', mismatch.
✅ Fix:
Avoid rendering different states initially. Show fallback:
const [value, setValue] = useState(null);
useEffect(() => {
setValue('Updated');
}, []);
if (value === null) return <Loading />;
Answered by Fozle Rabbi ·
Mismatched Third-party Libraries (like date formatting)
Cause:
Libraries like moment or dayjs can show locale-based or timezone-sensitive data.
❌ Problem Code:
import dayjs from 'dayjs';
export default function Page() {
return <div>{dayjs().format('HH:mm')}</div>;
}
✅ Fix:
Render such time-based data only on client:
'use client';
Or use server-time consistently and cache the value.
Answered by Sahin Khan ·
Inconsistent Environment Variables
Cause:
Rendering depends on env variables that differ in server vs. browser.
❌ Example:
<div>{process.env.NEXT_PUBLIC_TITLE}</div>
If not prefixed with NEXT_PUBLIC_, they won’t be available on client.
✅ Fix:
Always prefix env vars for client usage:
NEXT_PUBLIC_TITLE=MyApp
Answered by Dipa Saha ·
Using window or document in Server Components
❌ Problem Code:
export default function Page() {
return <div>Width: {window.innerWidth}</div>;
}
This breaks during SSR, as window is undefined.
✅ Fix:
'use client';
import { useEffect, useState } from 'react';
export default function Page() {
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
return <div>Width: {width}</div>;
}
Answered by Mynul Islam ·
Server and client generate different values.
❌ Problem Code:
export default function Page() {
return <div>Random number: {Math.random()}</div>;
}
✅ Fix:
Use such logic only on the client:
'use client';
import { useEffect, useState } from 'react';
export default function Page() {
const [random, setRandom] = useState(0);
useEffect(() => {
setRandom(Math.random());
}, []);
return <div>Random number: {random}</div>;
}
Answered by Ratul Sikder ·
Rendering Based on navigator, localStorage, etc.
❌ Problem:
const isDark = localStorage.getItem('theme') === 'dark';
This causes crash/mismatch on server.
✅ Fix:
'use client';
import { useEffect, useState } from 'react';
export default function Page() {
const [theme, setTheme] = useState('light');
useEffect(() => {
setTheme(localStorage.getItem('theme') || 'light');
}, []);
return <div>Current theme: {theme}</div>;
}
Answered by Shaykot Hossain Selim ·
Cause:
If server and client fetch data differently, mismatch can happen.
❌ Problem Code:
export default async function Page() {
const res = await fetch('<https://api.example.com/data>');
const { message } = await res.json();
return <div>{message}</div>;
}
If data changes between SSR and client hydration, mismatch occurs.
✅ Fix:
Use caching (e.g., fetch with next/cache) or move to client:
// Use revalidation and caching
export const dynamic = 'force-static';
Or defer to the client if live data is essential:
'use client';
