React hydration and server-side rendering (SSR)
What is Server-Side Rendering (SSR)?
SSR renders React components to HTML on the server and sends the fully rendered HTML to the browser. The browser shows the HTML immediately (fast First Contentful Paint), then React takes over to make it interactive.
The SSR + Hydration Flow
1. User requests page
2. Server renders React โ HTML string
3. HTML sent to browser โ user sees content immediately
4. Browser downloads React JS bundle
5. React "hydrates" the HTML โ attaches event listeners
6. Page is now fully interactiveWhat is Hydration?
Hydration is the process where React attaches event handlers and state to the existing server-rendered HTML, without re-rendering it. React "reuses" the DOM instead of creating it from scratch.
// Server renders this HTML:
// <button>Clicked 0 times</button>
// Client hydrates โ attaches onClick handler to existing button
// No new DOM elements created โ React reuses the server HTMLSSR vs CSR vs SSG
| Approach | When HTML is generated | Performance | SEO |
|---|---|---|---|
| CSR (Client-Side) | In the browser | Slow FCP, fast navigation | Poor |
| SSR (Server-Side) | On each request | Fast FCP, server cost | Great |
| SSG (Static Gen) | At build time | Fastest | Great |
| ISR (Incremental) | Build + revalidation | Fast + fresh | Great |
Hydration Errors
When server HTML doesn't match what React expects on the client:
// โ Causes hydration mismatch
function Clock() {
return <p>{new Date().toLocaleTimeString()}</p>;
// Server: "10:00:00 AM"
// Client: "10:00:01 AM" โ DIFFERENT!
}
// โ
Fix: render time only on client
function Clock() {
const [time, setTime] = useState<string>("");
useEffect(() => {
setTime(new Date().toLocaleTimeString());
const interval = setInterval(() => {
setTime(new Date().toLocaleTimeString());
}, 1000);
return () => clearInterval(interval);
}, []);
return <p>{time || "Loading..."}</p>;
}Common Causes of Hydration Errors
- Using
Date.now(),Math.random()in render - Browser-only APIs (
window,localStorage) in render - Different data on server vs client
- Invalid HTML nesting (
<p><div>...</div></p>) - Browser extensions modifying the DOM
Selective Hydration (React 18)
React 18 can hydrate different parts of the page independently using Suspense:
import { Suspense } from "react";
function Page() {
return (
<div>
<Header /> {/* Hydrates immediately */}
<Suspense fallback={<Spinner />}>
<HeavySidebar /> {/* Hydrates when ready */}
</Suspense>
<Suspense fallback={<Skeleton />}>
<Comments /> {/* Can hydrate independently */}
</Suspense>
</div>
);
}With selective hydration:
- React can hydrate non-blocking โ other parts stay interactive
- If user clicks on a not-yet-hydrated section, React prioritizes it
Streaming SSR
React 18 also introduced streaming โ sending HTML in chunks:
// Server streams HTML progressively
// 1. Send <html>, <head>, <Header /> immediately
// 2. When data is ready, stream <MainContent />
// 3. When comments load, stream <Comments />
// Each Suspense boundary is a streaming point
<Suspense fallback={<Loading />}>
<SlowComponent /> {/* Streamed when ready */}
</Suspense>Benefits:
- Users see content sooner (progressive loading)
- Time to First Byte (TTFB) is faster
- No need to wait for ALL data before sending response
Important:
SSR generates HTML on the server for fast initial display and SEO. Hydration then makes it interactive on the client. React 18 improved this with selective hydration and streaming SSR, allowing non-blocking progressive rendering. Watch out for hydration errors caused by server/client content mismatches.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.