A hydration failure means React received server-rendered HTML, but the client's first render produced different text or structure. The durable fix is to locate the unstable output—not to silence the warning across an entire component tree.
TOC
- The most common causes
- Dates and random values
- Browser APIs and local storage
- Invalid HTML nesting
- Data snapshot mismatches
- A practical debugging sequence
- When to use suppressHydrationWarning
- Client Components do not automatically solve it
- References
The most common causes
- Calling
Date.now(),new Date(), orMath.random()while rendering. - Returning different JSX from a
typeof window !== "undefined"branch. - Reading
localStorage, viewport size, or media queries during the first render. - The server and client rendering different data snapshots.
- Invalid HTML nesting that the browser repairs before React starts.
- A browser extension or third-party script changing the DOM.
- Different locale, time-zone, or number-formatting behavior.
Dates and random values
This output is not stable:
export function Timestamp() {
return <time>{new Date().toLocaleString()}</time>
}
The server and browser may use different times, locales, and time zones. Pass a stable value from the server instead:
export function Timestamp({ iso }: { iso: string }) {
return <time dateTime={iso}>{iso.slice(0, 10)}</time>
}
If the interface must display the viewer's local time, render a stable placeholder first and update it after hydration. Reserve enough space to avoid an unnecessary layout shift.
Browser APIs and local storage
Do not make the first render depend on browser detection:
// Incorrect: the server and client can return different text
return <p>{typeof window === "undefined" ? "server" : "client"}</p>
Read client-only values in an effect when appropriate:
"use client"
import { useEffect, useState } from "react"
export function ThemeLabel() {
const [theme, setTheme] = useState("system")
useEffect(() => {
setTheme(localStorage.getItem("theme") ?? "system")
}, [])
return <span>Theme: {theme}</span>
}
Both environments initially render system; the browser synchronizes the stored value afterward. If the correct theme is required before the first paint, use a server-readable cookie or a carefully tested initialization script.
Invalid HTML nesting
Browsers repair invalid markup, so the DOM React hydrates may not match the original server string:
// Incorrect
<p>
Introduction
<div>Details</div>
</p>
Inspect the actual Elements tree, not just the component source. Rich-text renderers, Markdown pipelines, and third-party UI components are common sources of invalid nesting.
Data snapshot mismatches
Typical causes include:
- the server reading cached version A while the client immediately fetches version B;
- different sorting or filtering rules in each environment;
- client code constructing a second default state instead of reusing server data;
- locale-dependent formatting without an explicit locale and time zone.
For the first screen, fetch data in a Server Component when possible, then pass the same snapshot into Client Components that need hydration.
A practical debugging sequence
- Read the component stack and the text difference in the error.
- Reduce the problem to the smallest suspicious Client Component.
- Search for
Date,Math.random,window,document,localStorage, and locale formatting. - Validate HTML nesting.
- Reproduce in a private browser window with extensions disabled.
- Compare View Source—the server HTML—with the post-hydration Elements DOM.
- Test a production build, not only the development server.
When to use suppressHydrationWarning
suppressHydrationWarning is a narrow escape hatch for a known, unavoidable single-level text difference. It does not repair data flow or a mismatched subtree.
<time suppressHydrationWarning>{clientSpecificTime}</time>
If structure, event binding, or business state differs, fix the rendering source. Adding the prop to a large wrapper only hides evidence.
Client Components do not automatically solve it
A Client Component can still participate in the initial server-rendered preview and then hydrate in the browser. Adding "use client" does not guarantee matching output. Keep the initial render deterministic and isolate truly browser-only behavior.
For another common Next.js boundary decision, see Server Actions vs client fetch.
References
Continue reading
Next.js Image Guide: sizes, fill, and Remote Images
Use the modern Next.js Image component with width and height, fill, sizes, preload, placeholders, remotePatterns, and clear rules for avoiding outdated layout props.
11 min readNext.js Routing Guide: App Router Patterns
Learn the current Next.js App Router model: static routes, dynamic segments, catch-all routes, layouts, route groups, parallel routes, and route handlers.
10 min readNext.js Server vs Client Components: How to Choose
Choose between Server Components and Client Components in the Next.js App Router by looking at data access, state, events, browser APIs, serialization, and bundle size.
Subscribe to FreeMac
Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.