How to debug and fix Next.js hydration errors
A hydration error means the HTML the server rendered is different from what React rendered in the browser. The fix is to find what differs, and make it the same or render it only in the browser.
What a hydration error is
Next.js renders your page to HTML on the server. In the browser, React then hydrates that HTML: it renders the same components again and attaches event handlers. If the two renders produce different output, React reports a hydration mismatch and may re-render the whole tree on the client. Recent Next.js versions show a diff of the server and client output in the error overlay, which usually points straight at the problem.
The usual causes
- Values that change between renders:
new Date(),Date.now(),Math.random(), generated IDs. - Browser-only checks during render:
typeof window !== 'undefined',localStorage, media queries. - Locale and time zone differences:
toLocaleString()on a server in UTC and a browser in another zone. - Invalid HTML nesting: a
<div>inside a<p>, or a link inside a link. The browser fixes the HTML before React sees it. - Browser extensions that change the page before React hydrates. Test in a private window.
The fixes
Render browser-only values after mounting
'use client';
import { useEffect, useState } from 'react';
export function LocalTime() {
const [time, setTime] = useState<string>();
useEffect(() => setTime(new Date().toLocaleTimeString()), []);
return <span>{time ?? '…'}</span>;
}
Skip server rendering for a browser-only component
'use client';
import dynamic from 'next/dynamic';
const Map = dynamic(() => import('./Map'), { ssr: false });
Silence one expected difference
For a single element whose text is meant to differ, such as a timestamp, add suppressHydrationWarning to that element. It only applies one level deep, so do not use it to hide real bugs.
Fix the HTML
Change the outer <p> to a <div>, or move the nested block outside it.
Finding the component
Read the diff in the overlay, then search for the text it shows. If the stack is not enough, pause on the error: in Chrome DevTools enable Pause on uncaught exceptions, or in qaisdebugs turn on On exceptions in the Sources view, which also records the error in its Logs view with the file it came from.