πŸ”§ Error Fixes

Hydration Mismatch Error (React / Next.js) β€” How to Fix It


Error: Hydration failed because the server rendered HTML didn't match the client.
Warning: Expected server HTML to contain a matching <div> in <div>.

React rendered different HTML on the server vs. the client. When the client tries to β€œhydrate” (attach event listeners to the server HTML), it finds a mismatch and throws this error.

Fix 1: Browser-Only Values

The most common cause. You’re using something that doesn’t exist on the server.

// ❌ window doesn't exist on the server
function App() {
  return <p>Width: {window.innerWidth}</p>;
}

// βœ… Use useEffect for browser-only values
function App() {
  const [width, setWidth] = useState(0);

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

  return <p>Width: {width}</p>;
}

Common culprits: window, document, localStorage, navigator, Date.now(), Math.random().

Fix 2: Date/Time Differences

Server and client are in different timezones or render at different times.

// ❌ Different time on server vs client
function App() {
  return <p>{new Date().toLocaleString()}</p>;
}

// βœ… Render on client only
function App() {
  const [time, setTime] = useState('');

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

  return <p>{time}</p>;
}

Fix 3: Invalid HTML Nesting

Browsers auto-correct invalid HTML, causing mismatches.

// ❌ <p> can't contain <div>
<p>
  <div>This breaks hydration</div>
</p>

// βœ… Use valid nesting
<div>
  <div>This works</div>
</div>

// ❌ <a> inside <a>
<a href="/parent">
  <a href="/child">Nested link</a>
</a>

Fix 4: Browser Extensions

Extensions like ad blockers, Grammarly, or translation tools modify the DOM, causing mismatches.

// Suppress hydration warnings on specific elements
<div suppressHydrationWarning>
  {/* Content that might be modified by extensions */}
</div>

This is a valid escape hatch for content you know might differ.

Fix 5: Conditional Rendering Based on Auth/State

// ❌ Server doesn't know if user is logged in
function Nav() {
  const user = getUser(); // null on server, object on client
  return user ? <LogoutButton /> : <LoginButton />;
}

// βœ… Use a loading state
function Nav() {
  const [user, setUser] = useState(null);
  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    setUser(getUser());
    setLoaded(true);
  }, []);

  if (!loaded) return null; // or a skeleton
  return user ? <LogoutButton /> : <LoginButton />;
}

Fix 6: Next.js Dynamic Import

For components that should only render on the client:

import dynamic from 'next/dynamic';

const Chart = dynamic(() => import('./Chart'), {
  ssr: false,
  loading: () => <p>Loading chart...</p>,
});

Quick Checklist

  1. Are you using window, document, or localStorage during render? β†’ Move to useEffect
  2. Are you rendering dates or random values? β†’ Move to useEffect
  3. Is your HTML nesting valid? β†’ Check <p>, <a>, <table> rules
  4. Does it only happen in dev? β†’ Might be a browser extension