Skip to main content
Practice Problems

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 interactive

What 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.

tsx
// 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 HTML

SSR vs CSR vs SSG

ApproachWhen HTML is generatedPerformanceSEO
CSR (Client-Side)In the browserSlow FCP, fast navigationPoor
SSR (Server-Side)On each requestFast FCP, server costGreat
SSG (Static Gen)At build timeFastestGreat
ISR (Incremental)Build + revalidationFast + freshGreat

Hydration Errors

When server HTML doesn't match what React expects on the client:

tsx
// โŒ 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:

tsx
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:

tsx
// 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems