/ blog/fixing-hydration-errors-react
blog / fixing-hydration-errors-react / overview.md

Fixing Hydration Errors in Next.js: A Structural Approach

Hydration errors are the bane of Next.js developers. Stop fighting the warnings and learn the structural causes behind 'Text content did not match server-rendered HTML'.

The Root Cause

Hydration is the process where React attaches event listeners to the static HTML sent by the server. If the DOM tree expected by React (based on its initial client render) doesn't exactly match the HTML tree from the server, React throws a hydration error and throws away the server HTML, re-rendering from scratch.

This destroys the performance benefits of SSR.

Common Culprits

1. Invalid HTML Nesting

React uses the browser's DOM parser. If your server sends invalid HTML, the browser will auto-correct it before React hydrates.

// INVALID: You cannot nest a <div> inside a <p>
export default function BadComponent() {
  return (
    <p>
      <div>This will cause a hydration error.</div>
    </p>
  );
}

The browser converts this to <p></p><div>...</div>. When React hydrates, it looks inside the <p> for the <div> and panics because it's empty.

2. Time and Dates

If you render new Date().toLocaleTimeString(), the server renders it at 10:00:00, and the client hydrates at 10:00:01. Mismatch.

3. Window and LocalStorage

Checking typeof window !== 'undefined' during the initial render loop will return false on the server and true on the client.

The Universal Fix

If you have a component that must render differently on the client (like checking localStorage for a theme), you must delay that rendering until after the initial hydration pass.

export function ClientOnly({ children }: { children: React.ReactNode }) {
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() => {
    setHasMounted(true);
  }, []);

  if (!hasMounted) {
    return null; // or a skeleton loader
  }

  return <>{children}</>;
}

By returning null on the first pass, both the server and client agree. Then, useEffect fires, triggering a safe re-render on the client only.

Tags

reactnextjsdebugging
0
0