• Next.js
  • Hydration
  • SSR
  • Debugging

Understanding and Fixing Hydration Errors in Next.js

The Complete Guide to Solving the 'Text content does not match' Warning

Amit Hariyale

Amit Hariyale

Full Stack Web Developer, Gigawave

8 min read · June 14, 2025

Hydration errors are among the most common and frustrating issues in Next.js development. These errors occur when there's a mismatch between server-rendered HTML and client-side JavaScript. This guide will help you understand why they happen and how to fix them permanently.

Why Hydration Errors Occur

  • Mismatch between server and client HTML
  • Browser-specific APIs used during server rendering
  • Inconsistent date/time formatting between server and client
  • Third-party libraries not SSR-compatible
  • Incorrect use of useEffect/useLayoutEffect hooks
Did You Know?Hydration errors only occur in development mode? In production, React will silently reconcile differences, potentially causing layout shifts!

The Classic Hydration Error Message

You've probably seen this warning in your console:

bash
1Warning: Text content did not match. Server: "Hello" Client: "World" 2Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.

Step 1: Understanding the Hydration Process

The Hydration ProcessThe hydration process in Next.js involves the following steps:
Server Rendering
Next.js generates HTML on the server
Initial Hydration
React attaches event handlers to server HTML
Mismatch Detection
React compares server and client output
Error Thrown
Differences trigger hydration error

Hydration is React's process of attaching to server-generated HTML and making it interactive.

Step 2: Common Causes and Fixes

Browser-Specific APIs

Accessing window or documentdirectly during server rendering:

components/BrokenComponent.tsx
1// ❌ Wrong: Accessing window during render 2function BrokenComponent() { 3 const width = window.innerWidth; // Server: undefined, Client: 1200 4 return <div>Width: {width}px</div>; 5}
components/FixedComponent.tsx
1// ✅ Fixed: Use useEffect + useState 2function FixedComponent() { 3 const [width, setWidth] = useState(0); 4 5 useEffect(() => { 6 setWidth(window.innerWidth); 7 const handleResize = () => setWidth(window.innerWidth); 8 window.addEventListener('resize', handleResize); 9 return () => window.removeEventListener('resize', handleResize); 10 }, []); 11 12 return <div>Width: {width}px</div>; 13}

Date/Time Mismatches

Formatting dates differently on server and client:

components/DateComponent.tsx
1// ❌ Wrong: Inconsistent formatting 2function DateComponent() { 3 const date = new Date().toLocaleDateString(); 4 // Server: "7/5/2025", Client: "05/07/2025" (different locales) 5 return <div>Today: {date}</div>; 6}
components/FixedDateComponent.tsx
1// ✅ Fixed: Use consistent timezone/locale 2function FixedDateComponent() { 3 const [date, setDate] = useState(''); 4 5 useEffect(() => { 6 setDate(new Date().toLocaleDateString('en-US')); 7 }, []); 8 9 return <div>Today: {date}</div>; 10}

Third-Party Library Issues

Libraries that aren't SSR-compatible:

components/MapComponent.tsx
1// ❌ Wrong: Directly using non-SSR library 2import { Map } from 'non-ssr-map-library'; 3 4function LocationMap() { 5 return <Map center={[51.505, -0.09]} />; // Fails on server 6}
components/FixedMapComponent.tsx
1// ✅ Fixed: Dynamic import with no SSR 2import dynamic from 'next/dynamic'; 3 4const Map = dynamic(() => import('non-ssr-map-library').then(mod => mod.Map), { 5 ssr: false 6}); 7 8function FixedLocationMap() { 9 return <Map center={[51.505, -0.09]} />; 10}

Step 3: Advanced Fixes

Suppressing Hydration Warnings

For safe differences (like unimportant whitespace):

pages/index.tsx
1// Add suppressHydrationWarning to ignore specific differences 2<div suppressHydrationWarning={true}> 3 {new Date().toLocaleTimeString()} 4</div>

Using useLayoutEffect

For DOM measurements that need to happen before paint:

components/MeasureComponent.tsx
1import { useLayoutEffect, useRef } from 'react'; 2 3function MeasureComponent() { 4 const ref = useRef(null); 5 6 useLayoutEffect(() => { 7 const { width } = ref.current.getBoundingClientRect(); 8 console.log('Width:', width); 9 }, []); 10 11 return <div ref={ref}>Content</div>; 12}

Pro Tips for Avoiding Hydration Errors

  • Always initialize state with consistent values
  • Use CSS for layout shifts instead of JS
  • Prefer data fetching in getServerSideProps/getStaticProps
  • Test with different locales and timezones
  • Verify third-party library SSR compatibility
Hydration Error ChecklistCommon issues that cause hydration errors
Browser APIs
Moved to useEffect/useLayoutEffect
Dates/Times
Consistent formatting on server/client
External Libraries
Using dynamic imports with ssr:false
State Initialization
Consistent initial values
CSS-in-JS
Proper server-side rendering config

Best Practices

  • Always render the same component tree on server and client
  • Use the 'use client' directive strategically
  • Abstract browser-specific code into custom hooks
  • Implement error boundaries for graceful failures
  • Use Next.js' built-in ESLint rules for hydration checks

Essential Resources

Final Thoughts

Hydration errors can be tricky, but understanding their root causes makes them much easier to solve. Remember that consistency between server and client rendering is key - when in doubt, move browser-specific logic to useEffect or useLayoutEffect hooks.

By following these patterns and best practices, you'll eliminate hydration errors and create more robust Next.js applications with smoother user experiences.

Next Blog

Mastering Next.js Routing From Basics to Advanced Patterns

Read Next