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]
Hydration failed in a brand-new Next.js app because the server and browser rendered different HTML.
This error usually appears in a freshly initialized project when a Server-Side Rendered tree includes output that changes between the server render and the first client render. In practice, that means something in the app is reading browser-only values, generating unstable markup, or wrapping a Client Component in logic that produces different HTML on each side.
In a newly created Next.js project, this can be triggered by project customizations added immediately after setup, including theme providers, browser extensions affecting markup, random values rendered during SSR, or third-party UI code that is not safe for hydration.
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 HTML generated on the server must match what React expects to render in the browser on the very first pass.
This error happens when those two versions differ. In Next.js, the most common causes are:
- Browser-only APIs used during render, such as
window,document,localStorage, ormatchMedia. - Non-deterministic values rendered during SSR, such as
Date.now(),Math.random(), generated IDs, or locale-sensitive formatting that differs between environments. - Client Components rendering different initial UI before
useEffectruns. - Theme or state restored from localStorage after the server already rendered a default value.
- Invalid HTML nesting, which causes the browser to rewrite the DOM before React hydrates it.
- Browser extensions injecting attributes or DOM nodes into the page before hydration.
In a new Next.js application, one especially common scenario is adding a provider or UI library to app/layout.tsx or app/page.tsx that computes values differently on the server and client. Another common issue is rendering conditional text like this:
{typeof window !== 'undefined' ? 'Client' : 'Server'}
That code guarantees a mismatch because the server renders Server while the browser immediately renders Client.
Step-by-Step Solution
The fix is to make the initial render deterministic. Render the same HTML on the server and the first client pass, then move browser-dependent logic into useEffect or load the component only on the client.
1. Check the exact component causing the mismatch
Start the dev server and inspect the error overlay. Then temporarily simplify your page until the mismatch disappears.
npm run dev
If the issue vanishes after removing a provider, custom theme wrapper, or third-party component, that code is the source.
2. Move browser-only logic into useEffect
Do not read window, document, or localStorage inside the render path.
Problematic code:
'use client'
export default function Page() {
const theme = localStorage.getItem('theme')
return <div>Current theme: {theme}</div>
}
Correct approach:
'use client'
import { useEffect, useState } from 'react'
export default function Page() {
const [theme, setTheme] = useState('light')
useEffect(() => {
const savedTheme = localStorage.getItem('theme')
if (savedTheme) setTheme(savedTheme)
}, [])
return <div>Current theme: {theme}</div>
}
This ensures the server and first client render both output light, then update safely after hydration.
3. Avoid rendering random or time-based values during SSR
Problematic code:
export default function Page() {
return <p>{Date.now()}</p>
}
Fix:
'use client'
import { useEffect, useState } from 'react'
export default function Page() {
const [time, setTime] = useState('')
useEffect(() => {
setTime(String(Date.now()))
}, [])
return <p>{time || 'Loading...'}</p>
}
If the value must come from the server, compute it on the server and pass it down consistently instead of regenerating it on the client.
4. Fix conditional rendering based on environment
Problematic code:
export default function Page() {
return <div>{typeof window !== 'undefined' ? 'Client' : 'Server'}</div>
}
Fix:
'use client'
import { useEffect, useState } from 'react'
export default function Page() {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
return <div>{mounted ? 'Client' : 'Loading...'}</div>
}
This pattern is especially useful for theme toggles, auth UI, and viewport-dependent rendering.
5. Isolate incompatible components with dynamic import
If a third-party library depends heavily on browser APIs, render it only on the client.
import dynamic from 'next/dynamic'
const ClientOnlyWidget = dynamic(() => import('./ClientOnlyWidget'), {
ssr: false,
})
export default function Page() {
return <ClientOnlyWidget />
}
Use this carefully. It solves hydration issues for browser-only widgets, but it also disables SSR for that component.
6. Validate HTML structure
Incorrect markup can cause the browser to auto-correct the DOM, which breaks hydration.
Problematic HTML:
<p><div>Invalid nesting</div></p>
Correct HTML:
<div>
<p>Valid nesting</p>
</div>
Review shared layout components, wrappers, and UI library outputs for invalid nesting.
7. Test without browser extensions
Extensions such as password managers, accessibility tools, or DOM injectors can alter markup before React hydrates. Test the app in an incognito window with extensions disabled. If the error disappears, your code may be correct and the mismatch is extension-driven.
8. If using a theme provider, add mount guards
Theme libraries often read user preference from localStorage or system settings. That can cause the server to render one theme and the browser to render another.
'use client'
import { useEffect, useState } from 'react'
export default function ThemeAwareUI() {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) return null
return <button>Toggle theme</button>
}
If you are using Next.js app router documentation patterns with a theme provider, verify that the provider is configured for client usage and does not produce different server/client markup.
9. Compare your app with a minimal known-good page
Replace the page temporarily with this:
export default function Page() {
return <main>Hello Next.js</main>
}
If this works, reintroduce custom code one piece at a time: layout, providers, imported components, then third-party packages. The exact reintroduction step that breaks hydration reveals the cause.
10. Clean install and verify package versions
If the project was copied, generated, or upgraded across versions, remove stale dependencies and reinstall.
rm -rf node_modules .next package-lock.json
npm install
npm run dev
Also confirm you are using compatible React and Next.js versions from the official documentation.
Common Edge Cases
- Locale mismatches:
toLocaleString()can output differently on server and client depending on runtime locale settings. - Generated IDs: Custom ID generation in render can differ between environments. Prefer stable data-driven IDs.
- Authentication state: Rendering signed-in UI on the client before the server knows the session can cause mismatch. Render a neutral loading state first.
- Media queries: Rendering based on screen width during SSR is unsafe unless the initial UI is consistent.
- Third-party UI kits: Some packages assume a browser environment and break under SSR unless dynamically imported with
ssr: false. - Strict Mode confusion: Double invocation in development can expose unstable render logic that seemed to work before.
- App Router provider placement: Putting a client-only provider too high in the tree without stable initial output can trigger full subtree regeneration.
FAQ
Is this a Next.js bug in a fresh project?
Usually no. In most cases, the base generated project is fine, and the mismatch starts after adding a provider, UI library, theme logic, or browser-dependent render code. The error message looks severe, but it typically points to unstable initial markup rather than a framework defect.
When should I use dynamic(..., { ssr: false })?
Use it for components that truly require the browser, such as editors, maps, charts, or widgets that access window during render. Do not use it as the first fix for every hydration error; first try making the initial render consistent.
Can browser extensions really cause hydration failed errors?
Yes. If an extension injects attributes, buttons, or wrappers before React hydrates, the DOM no longer matches the server-rendered HTML. Always test in a clean browser session before assuming your code is wrong.
The core rule is simple: the first client render must match the server render exactly. Once you enforce that rule by moving browser-only logic into effects, using stable values, and isolating non-SSR-safe libraries, the hydration failure disappears reliably.