Next.js Hydration Error: Causes and Solutions
Discover the real causes of the Next.js hydration error and learn how to definitively fix HTML, date, and time zone discrepancies.

What is the Next.js Hydration Error and Why Does It Happen
The Next.js hydration error occurs when there is a discrepancy between the React tree pre-rendered on the server and the component tree generated during the first render within the browser. Hydration is the process by which React converts pre-rendered HTML from the server into an interactive application, attaching event handlers to DOM elements.
When React detects that the HTML sent by the server does not exactly match what the client rendered on initialization, it triggers the hydration error warning. Resolving this issue requires identifying where the server and client structures diverged, ensuring the code behaves identically in both environments before interactivity.
When structuring your project, ensuring code runs consistently on the server and in the browser prevents rendering failures. If you are developing or planning how long it takes to build a website, mastering the hydration flow is essential for the quality of your production code.
Key Points
- Essential Concept: Hydration is when React attaches event handlers to the HTML coming from the server.
- Root Cause: Differences in HTML markup, date/time zone data, or browser APIs between the server and the client.
- iOS Behavior: Apple devices automatically convert numbers and emails into links if not configured.
- Official Solutions: Using
useEffect, dynamic import withnext/dynamic, and thesuppressHydrationWarningprop.
Main Causes of Hydration Errors in Next.js Projects
The official Next.js documentation on hydration errors details scenarios that cause inconsistencies between server pre-rendering and the browser.
Incorrect HTML Tag Nesting
The browser attempts to automatically correct invalid HTML before React hydrates the page. When the markup sent by the server has incorrectly nested elements, the DOM tree is restructured by the browser, resulting in a divergence from the React tree. Common cases include:
- A paragraph (
<p>) inside another paragraph (<p>). - A
<div>inserted inside a paragraph (<p>). - A list (
<ul>or<ol>) inside a paragraph (<p>). - Nested interactive content, such as a link (
<a>) inside another link (<a>) or a button (<button>) inside another button (<button>).
Using Browser-Exclusive APIs and typeof window Checks
Calling APIs that exist only in the client environment (like window or localStorage) during the rendering phase causes the server to be unable to execute the same code snippet. Similarly, adding conditional checks like typeof window !== 'undefined' directly into the rendering logic alters the generated structure: the server renders one branch, and the browser renders another on the first render.
Date() Constructor, Dates, and Time Zones
Time-dependent APIs, such as the Date() constructor, are frequent sources of errors. If the Next.js server processes the request configured with the UTC time zone, and the user's browser is in the São Paulo time zone, the text displayed on the screen will differ on both sides. This difference in the generated HTML breaks hydration. As discussed in the next-intl repository discussions on GitHub, managing localization and time zone data requires extra attention.
Automatic iOS Safari Behavior
On iOS devices, the operating system automatically injects links over numeric sequences (which it interprets as phone numbers) and email addresses found in the text. This modification of the HTML directly by the system creates extra nodes that did not exist on the server, triggering the hydration error.
Extensions, CSS-in-JS, and CDN/Edge Services
Other external factors also alter the response received by the browser:
- Browser Extensions: Add-ons that modify the DOM tree by inserting scripts or elements.
- Misconfigured CSS-in-JS Libraries: When critical CSS extraction is not synchronized between server and client.
- CDN and Edge Networks: Services that alter the server's response. A nominal example cited in the Next.js documentation is Cloudflare's Auto Minify feature, which modifies HTML before delivering it to the client.
"Hydration breaks whenever the HTML sent by the server does not perfectly match the first render result on the client."
Comparison Table: Error Cause vs. Recommended Fix
| Divergence Cause | Problem Origin | Recommended Fix |
|---|---|---|
| Invalid Nesting | Semantically incorrect HTML (e.g., div in p) |
Correct HTML tags to respect DOM specification |
Browser APIs (localStorage, window) |
Access to data that doesn't exist on the server | Execute reads within useEffect with an isClient state |
| Divergent Dates and Time Zones | Server in UTC and client in local time zone | Use Intl.DateTimeFormat with a fixed timeZone or format on the server |
| iOS Auto-Formatting | System converts text to phone/email links | Add format-detection meta tag in the layout |
| Client-Only Components | Heavy browser dependency | Load via next/dynamic with { ssr: false } |
How to Resolve Hydration Errors Step-by-Step
Detailed problem analyses, like the technical article published on the OneUptime blog, reinforce that there are specific approaches for each cause of misalignment. Below are the practical steps to fix each scenario.
Step 1: Adjust Client-Dependent Code with useEffect
For code snippets that rely on window or localStorage, use the useEffect hook. The server will render a safe initial state, and after hydration, React will update the screen on the client.
'use client';
import { useState, useEffect } from 'react';
export default function UserProfile() {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
if (!isClient) {
return <div>Loading...</div>;
}
const theme = localStorage.getItem('theme');
return <div>Current theme: {theme}</div>;
}
Step 2: Disable SSR in Specific Components with next/dynamic
If you have a component that entirely depends on browser APIs and doesn't need server-side rendering, disable SSR (Server-Side Rendering) using the dynamic function from next/dynamic.
import dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(
() => import('../components/ClientOnly'),
{ ssr: false }
);
export default function Page() {
return (
<main>
<h1>My Page</h1>
<ClientOnlyComponent />
</main>
);
}
Step 3: Ensure Consistency in Dates and Time Zones
To avoid date discrepancies between the server (which might operate in UTC) and the client (in the São Paulo time zone), explicitly set time zone options using Intl.DateTimeFormat or format the string directly on the server without recalculating it in the browser.
export default function FormattedDate({ date }: { date: Date }) {
const formattedDate = new Intl.DateTimeFormat('en-US', {
dateStyle: 'full',
timeStyle: 'medium',
timeZone: 'America/New_York',
}).format(date);
return <time>{formattedDate}</time>;
}

Step 4: Block iOS Auto-Formatting with a Meta Tag
To prevent iOS from inserting automatic link tags into numbers and emails in the text, add the format-detection meta tag to your application's layout or header file.
export const metadata = {
other: {
'format-detection': 'telephone=no, date=no, email=no, address=no',
},
};
In traditional HTML or legacy pages, the tag corresponds to the following format:
<meta name="format-detection" content="telephone=no, date=no, email=no, address=no" />
Step 5: Use the suppressHydrationWarning Prop Consciously
The Next.js documentation provides the suppressHydrationWarning prop to tell React not to warn about discrepancies on a specific element. It's useful for rapidly changing timestamps but should be used with caution due to its three explicit caveats:
- It only works one level deep on the element where it's applied.
- It's an escape hatch that should not be overused in code.
- React does not attempt to fix diverging text content when the prop is active, keeping the server's value visible until another update.
export default function Timestamp() {
return (
<span suppressHydrationWarning>
{new Date().toLocaleTimeString()}
</span>
);
}
Frequently Asked Questions
Why is scattering suppressHydrationWarning throughout the code a bad practice?
Because the suppressHydrationWarning prop only acts as a warning suppressor and does not fix the root cause of the divergence. Furthermore, it only operates one level deep and causes React to ignore text discrepancies, which can mask serious visual bugs in the application.
How does Cloudflare Auto Minify affect hydration in Next.js?
Cloudflare's Auto Minify feature modifies the HTML code sent by the server before it reaches the client's browser. Since React on the client expects to receive the exact same HTML structure it generated on the server, this external alteration causes a mismatch during hydration.
What happens if I render the Date() constructor directly in JSX?
Since the execution occurs at different times and locations, the server will generate a date string based on the request time and server time zone, while the client will generate another string based on the user's computer clock. This text difference between the server's HTML and the client's breaks React's hydration process.
Conclusion
Resolving the Next.js hydration error requires understanding the origin of the discrepancy rather than simply hiding the warning. By identifying HTML nesting errors, isolating client-side code with useEffect, fixing time zones in dates, and disabling iOS modifiers, your Next.js application will achieve stability and high performance in production.

About Lee Sugano
Lee Sugano
Digital solutions agency based in Japan, serving clients in 10+ countries. We share insights on development, design and digital marketing for companies that don't settle for generic.
Enjoyed this content?
Receive exclusive insights about web development, design, and digital marketing straight to your inbox.
No spam. Unsubscribe anytime.


