How to Fix: Error in newly initialized Next project [Hydration failed because the server rendered HTML didn’t match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used]
A fresh Next.js app throwing a hydration failed error usually means the HTML generated on the server is different from what React renders in the browser. In this specific setup, the problem commonly appears when a Client Component depends on browser-only values or generates non-deterministic markup during the first render.
You can review the reproduction from the project repository.
Understanding the Root Cause
Hydration is the process where React attaches client-side behavior to HTML already rendered by the server. For hydration to succeed, the first client render must produce the exact same DOM structure and text content as the server output.
This error appears when a component marked with ‘use client’ renders different content between server and client. In newly initialized Next projects, the most common reasons are:
- Using window, document, localStorage, or sessionStorage directly during render.
- Rendering values from Date.now(), Math.random(), or locale-dependent formatting on the first render.
- Conditional rendering based on browser state such as screen size, theme, or persisted cart data before the component mounts.
- Using third-party UI libraries that generate different IDs or markup on server and client.
- Invalid HTML nesting causing React to rebuild part of the tree.
In an e-commerce starter, a very common pattern is reading cart state, theme, or auth data from localStorage inside the component body. The server cannot access that data, so it renders a fallback state. Then the browser reads the real value and renders something different, triggering the mismatch.
Example of a problematic pattern:
'use client'
export default function CartBadge() {
const cart = JSON.parse(localStorage.getItem('cart') || '[]')
return <span>{cart.length}</span>
}
On the server, localStorage does not exist. Even if guarded, the server might render 0 while the client renders 3 immediately, which is enough to break hydration.
Step-by-Step Solution
The fix is to make the initial render deterministic. Render the same placeholder or initial state on both server and client, then load browser-only data after mount.
1. Find browser-only code inside render
Search your project for these patterns inside components:
window
document
localStorage
sessionStorage
Date.now()
Math.random()
If any of them affect rendered output before mount, refactor them.
2. Move browser-only reads into useEffect
Use state with a stable default, then update it after the component mounts.
'use client'
import { useEffect, useState } from 'react'
export default function CartBadge() {
const [count, setCount] = useState(0)
useEffect(() => {
const cart = JSON.parse(localStorage.getItem('cart') || '[]')
setCount(cart.length)
}, [])
return <span>{count}</span>
}
This works because both server and first client render output 0, so hydration succeeds. The UI updates only after the component mounts.
3. Guard rendering until the client is mounted when necessary
If a component depends heavily on browser APIs and cannot render meaningful SSR output, delay it until mount.
'use client'
import { useEffect, useState } from 'react'
export default function ClientOnlyWidget() {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) return null
return <div>Client-only content</div>
}
Use this sparingly because it removes the SSR benefit for that component.
4. Disable SSR for incompatible third-party components
If a library is not SSR-safe, import it dynamically.
import dynamic from 'next/dynamic'
const ClientWidget = dynamic(() => import('./ClientWidget'), {
ssr: false,
})
export default function Page() {
return <ClientWidget />
}
This is often the cleanest solution for carousels, editors, charts, or theme widgets that rely on the browser.
5. Fix unstable values in JSX
Avoid this:
export default function Header() {
return <p>{Date.now()}</p>
}
Use state initialized after mount instead:
'use client'
import { useEffect, useState } from 'react'
export default function Header() {
const [timestamp, setTimestamp] = useState('')
useEffect(() => {
setTimestamp(String(Date.now()))
}, [])
return <p>{timestamp}</p>
}
6. Verify valid HTML structure
Incorrect nesting can also trigger hydration issues. For example, avoid placing a div inside a p tag or rendering inconsistent wrappers between conditions.
// Bad
return <p><div>Item</div></p>
// Good
return <div><p>Item</p></div>
7. Restart and test in development
After changes:
rm -rf .next
npm install
npm run dev
Then reload the page and check the terminal and browser console for the first component named in the hydration warning. That component is usually where the mismatch starts.
8. Practical fix for persisted cart, theme, or auth state
If your storefront stores UI state in the browser, create a mounted-safe pattern like this:
'use client'
import { useEffect, useState } from 'react'
export default function PersistedValue() {
const [value, setValue] = useState(null)
useEffect(() => {
const stored = localStorage.getItem('value')
setValue(stored)
}, [])
if (value === null) {
return <span>Loading...</span>
}
return <span>{value}</span>
}
The key idea is simple: the server and first client pass must agree on the same initial markup.
Common Edge Cases
- Theme toggles: Reading dark mode from localStorage in the render path often causes mismatch. Initialize after mount or use a library documented as SSR-safe.
- Authentication state: If navbar content depends on a token from browser storage, render a neutral placeholder first.
- Random IDs: Some component libraries generate IDs differently on server and client. Check if the library supports SSR or disable SSR for that widget.
- Locale/timezone formatting: toLocaleString() can render differently between server environment and browser.
- Conditional wrappers: Returning different parent elements based on client-only conditions can break hydration even when the visible text looks the same.
- Strict Mode confusion: In development, React may render components more than once, exposing hydration bugs more clearly. The root issue is still unstable output.
- Async data mixed with client state: If server-fetched product data renders one structure and client state immediately changes it, keep the first render stable.
FAQ
Why does this happen in a brand-new Next.js project?
The base framework is usually not the problem. The error typically appears after adding a Client Component, UI library, or browser storage logic that renders different markup on server and client.
Should I fix this with suppressHydrationWarning?
Usually no. suppressHydrationWarning hides the warning for specific text mismatches, but it does not fix the underlying inconsistency. Prefer making the initial render deterministic.
When should I use dynamic import with ssr: false?
Use it when a component fundamentally depends on browser-only APIs or a third-party package is not compatible with SSR. For simple storage reads or timestamps, useEffect is usually the better fix.
If your Next e-commerce project is failing right after initialization, start by auditing every ‘use client’ component that touches browser APIs or renders non-stable values. In most cases, moving that logic into useEffect or making the component client-only resolves the hydration mismatch immediately.