React  

How to fix hydration mismatch errors in React and Next.js apps?

Introduction

Hydration mismatch errors are common in React and even more common in Next.js, where server-side rendering (SSR) and client-side rendering must match perfectly.
You may have seen this error:

Warning: Text content does not match server-rendered HTML.

or

Hydration failed because the initial UI does not match what was rendered on the server.

This error means the HTML generated on the server differs from the HTML React generates in the browser.
In this article, we explain hydration mismatch errors in simple terms and show you, step by step, how to fix them in React and Next.js applications.

1. What Is Hydration in React and Next.js?

When using SSR (Server-Side Rendering), React sends pre-rendered HTML from the server.

Then the browser runs JavaScript to attach event listeners and activate the UI — this process is called hydration.

Hydration mismatch happens when:

  • Server-rendered HTML ≠ Client-rendered HTML

  • React cannot match DOM nodes

  • Values are different between the server and the client

This leads to warnings or even UI breaking in Next.js.

2. Common Causes of Hydration Mismatch

Hydration mismatch usually happens because the server and client render different UI.

Most common reasons:

  • Using browser-only APIs (window, document) during initial render

  • Rendering different values on the server and client

  • Using random values during render (like Math.random())

  • Time-based UI differences (new Date())

  • Conditional rendering that works only on the client

  • Fetching data differently on server and client

  • Race conditions with async components

Understanding the cause is the first step to fixing the issue.

3. Fix 1: Prevent Browser-Only Code from Running on the Server

React and Next.js SSR runs on the server where window and document do not exist.

❌ Wrong

const width = window.innerWidth;

✔ Fix using useEffect

const [width, setWidth] = useState(0);

useEffect(() => {
  setWidth(window.innerWidth);
}, []);

Why It Works

  • useEffect runs only on the client

  • Server render will not fail

4. Fix 2: Avoid Time-Based or Random Values During Render

❌ Wrong

<p>{new Date().toISOString()}</p>

❌ Wrong

<p>{Math.random()}</p>

These values differ between server and client.

✔ Fix

const [time, setTime] = useState(null);

useEffect(() => {
  setTime(new Date().toISOString());
}, []);

5. Fix 3: Wrap Client-Only Components in "use client" (Next.js 13+)

If you are using Next.js 13 with the App Router:

Example

"use client";

export default function ClientOnlyComponent() {
  return <div>Client side only</div>;
}

Why?

  • Prevents Next.js from rendering this component on the server

  • Eliminates SSR mismatches

6. Fix 4: Use Dynamic Import with SSR Disabled

If a component depends on browser APIs or behaves differently on the client, disable SSR.

Example

import dynamic from "next/dynamic";

const MapComponent = dynamic(() => import("../components/MapComponent"), {
  ssr: false,
});

export default function Page() {
  return <MapComponent />;
}

Why It Works

  • Component renders only on the client

  • No hydration conflicts

7. Fix 5: Ensure Initial State Matches on Server and Client

❌ Wrong (Server: 0, Client: 10)

const [count] = useState(window.innerWidth);

✔ Fix

const [count, setCount] = useState(0);

useEffect(() => {
  setCount(window.innerWidth);
}, []);

Initial state must match in SSR.

8. Fix 6: Avoid Conditional Rendering That Changes Between Server and Client

❌ Wrong

{typeof window !== "undefined" && <Sidebar />}

This causes SSR to render nothing, and client to render <Sidebar /> — mismatch.

✔ Fix using dynamic import

const Sidebar = dynamic(() => import("./Sidebar"), { ssr: false });

9. Fix 7: Use useEffect Instead of useLayoutEffect on the Server

React warns when using useLayoutEffect during SSR.

✔ Fix

useEffect(() => {
  // safe for client
}, []);

If you must use it, wrap it:

const useIsomorphicLayoutEffect =
  typeof window !== "undefined" ? useLayoutEffect : useEffect;

10. Fix 8: Ensure Data Fetching Uses the Same Source

Next.js App Router allows server components and client components.

If the server fetches data differently from the client, HTML will differ.

Fix

  • Use getServerSideProps (Pages Router)

  • Use Server Components for fetching in App Router

  • Pass data as props to client components

11. Fix 9: Use Suspense and Streaming Properly (Next.js 13+)

Streaming can cause UI mismatches if not coordinated.

Tips

  • Wrap async components inside <Suspense>

  • Ensure fallback UI matches between server and client

12. Debugging Hydration Errors (Important)

1. Check Browser Console

It shows exact element mismatch.

2. Add temporary logs

console.log("Server HTML:", htmlString);
console.log("Client Render", element);

3. Disable JavaScript temporarily

If server UI looks different → mismatch exists.

4. Use React DevTools Hydration Debug Mode (React 18+)

Helps locate mismatched components.

13. Best Practices to Avoid Hydration Mismatches

  • Keep server and client initial state identical

  • Avoid browser-only code in SSR

  • Avoid random or time-based values during render

  • Use useEffect for client-only logic

  • Prefer dynamic(..., { ssr: false }) for browser components

  • Use Server Components for fetching data

  • Test with JavaScript disabled to verify SSR output

Conclusion

Hydration mismatch errors in React and Next.js happen when the HTML rendered on the server does not match what React renders in the browser. By avoiding browser-only code during SSR, ensuring consistent state, handling time-based and random values correctly, and using Next.js features like dynamic imports and client components, you can completely eliminate hydration issues. With these best practices, your React and Next.js apps will become more stable, error-free, and production-ready.