Why setState Doesn’t Cause Hydration Errors
🚀 The Core Idea
Hydration errors happen only if the first client render output doesn’t match the server-rendered HTML.
Most setState
calls you write run after hydration is finished, so React treats them as normal updates, not as mismatches.
🕒 Timeline of hydration
-
Server render
- HTML is generated on the server and sent to the browser.
-
Client boot
- React runs the first client render and hydrates the existing DOM.
- It compares this client render output with the server HTML.
- If they differ, React shows a hydration mismatch warning.
-
After hydration
useEffect
callbacks run.- User events (clicks, inputs) happen.
- Calling
setState
here triggers a normal re-render; React patches the DOM without comparing to the server again.
✅ So if your initial client render matches the server, later setState
calls will not cause hydration errors.
❌ When you do get hydration errors
Hydration warnings occur if the first client render differs from the server HTML. Common causes include:
-
Non-deterministic values during render
// BAD: server vs client will produce different numbers const id = Math.random();
-
Browser-only values in render
// BAD: only available on client const width = window.innerWidth;
-
Different data fetching paths between server and client.
✅ Safe Patterns
1. Update inside useEffect
"use client";
import { useState, useEffect } from "react";
export default function Example() {
const [theme, setTheme] = useState<string | null>(null); // null matches server
useEffect(() => {
setTheme(window.localStorage.getItem("theme")); // runs after hydration
}, []);
return <div>Theme: {theme ?? "system"}</div>;
}
2. Mounted flag for client-only UI
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null; // matches server's "nothing"
return <ClientOnlyUI />;
3. Disable SSR for the component
In Next.js, you can skip SSR entirely:
import dynamic from "next/dynamic";
const ClientComponent = dynamic(() => import("./ClientComponent"), { ssr: false });
4. Suppress warnings (last resort)
For known benign differences, use:
<span suppressHydrationWarning>{clientOnlyValue}</span>
🧠 Mental Model
- Hydration compares only once: server HTML vs first client render.
setState
after hydration = normal re-render, no mismatch.
So: make sure your first render is deterministic, and you’re safe.
How did you like this post?
👍0
❤️0
🔥0
🤔0
😮0