Hydration: SSR HTML to interactive app?
understanding server rendering and client takeover.
server renders HTML, client JavaScript bootstraps and attaches event listeners (hydration), mismatch occurs if server HTML differs from client render.
WHY IT EXISTS: Server-side rendering outputs HTML (fast first paint, good SEO). But HTML is inert; interactivity requires JavaScript. Hydration bridges the gap: the client boots up, runs your framework code, and attaches event listeners to the existing DOM instead of re-rendering. If server HTML differs from what the client would render, hydration fails and the app is broken (clicks don't work, state is misaligned).
THE MENTAL MODEL: Server renders your component to HTML string (e.g., <button>Click me</button>). Browser paints it instantly. Client JavaScript loads, hydrates the page: the framework boots, renders the component to the same HTML in memory, and if it matches, reuses the existing DOM nodes and attaches listeners. Done. If server HTML was <button>Click me</button> but client would render <button>Click me!</button> (note the exclamation), they mismatch, and hydration fails (framework falls back to deleting the DOM and re-rendering, causing a flash and losing form state).
HOW IT WORKS: Ensure server and client produce identical HTML. Common mismatches: (1) Random data: server and client seed random values differently; (2) Timestamps: server uses current time, client uses client time; (3) DOM APIs: server code accesses window or document (not available on server), so server renders one thing, client renders another; (4) Component logic: conditional rendering that depends on client-only state (browser, device type). Debug: browser DevTools show warnings (Nuxt, SvelteKit, Angular Universal all warn on mismatch). Use SSR debugging: add logging in your component's setup/ngOnInit to see what server/client actually render. Compare the HTML served with what the browser renders after hydration. Wrap client-only code in if (typeof window !== 'undefined') { ... } to prevent server executing it.
WHEN IT MATTERS: Any SSR app needs hydration. Mismatches are silent killers: users see the page but clicks and forms don't work, or the page flashes (hydration failure triggers re-render). Mobile apps with large JS bundles are especially at risk (long delay between HTML render and hydration complete).
ONE CONCRETE EXAMPLE: A Nuxt page showing current time. Server renders Time: 2:00 PM at request time. Client hydrates, runs the component, state is reactive, time updates to 2:01 PM. But if the component calls new Date().toLocaleTimeString(), server and client timestamps differ (network latency), so Time: 2:00 PM (server) vs Time: 2:01 PM (client) mismatch. Browser console warns. Fix: don't call Date during SSR; set time on client after hydration (onMounted) or use a fixed value for both.
Read the original → vuejs.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.