Day 29: Next.js & Web Rendering: SSR vs SSG vs ISR vs CSR
Choose the right rendering strategy per route by understanding exactly WHEN each one generates HTML and what that trades off.
Study
Concepts
Four strategies, one question: when does HTML get built?
CSR (Client-Side Rendering): the server sends a near-empty HTML shell + a JS bundle; the browser downloads, executes, and renders everything. Fast to build, cheap to host (a static bundle), but slow first paint and poor SEO without extra work — the classic create-react-app model. SSG (Static Site Generation): HTML is built once, at BUILD TIME, for every page, then served instantly from a CDN — fastest possible response, but content is only as fresh as the last deploy, which makes it wrong for frequently-changing data.
SSR (Server-Side Rendering): HTML is built on EVERY REQUEST, on the server, using fresh data, then sent to the browser (which then hydrates it into an interactive React app). Always fresh, good SEO, but adds server compute cost and per-request latency. ISR (Incremental Static Regeneration): pages are built like SSG (fast, cached), but Next.js will regenerate a page in the background after `revalidate` seconds have passed since the last build, serving the (slightly) stale cached version instantly while the fresh one is prepared — combining SSG's speed with SSR-like freshness.
Hydration is the thread connecting all of them
Except pure CSR, every strategy sends pre-rendered HTML for fast first paint, then React "hydrates" it — attaching event listeners and internal state to the existing DOM nodes instead of re-creating them, so the visible content appears instantly while interactivity comes online a moment later. A mismatch between server-rendered HTML and what the client would render (e.g. using `Date.now()` or `window` during render) causes a hydration error — a very common real-world Next.js bug.
React Server Components (Next.js App Router) go further: some components render ONLY on the server and never ship their code to the client at all, reducing bundle size, while Client Components (`"use client"`) hydrate as usual — mixing both per-component, not per-page, is the current frontier of this tradeoff space.
See It
Visualizations
Visualization
When is the HTML for a request actually built?
| Static (SSG / ISR) | Dynamic (SSR / CSR) | |
|---|---|---|
| SSG: HTML built | Once, at deploy/build time | — |
| ISR: HTML built | At build, then re-built in background every N seconds | — |
| SSR: HTML built | — | On every request, on the server |
| CSR: HTML built | — | In the browser, after JS downloads & runs |
| Best for | Marketing pages, blogs, docs | Dashboards (CSR), personalized/real-time pages (SSR) |
Build It
Code Examples
The four strategies in Next.js App Router
// SSG (default for a page with no dynamic data access) —
// built once at deploy time, served from cache/CDN forever.
export default async function BlogPost({ params }) {
const post = await getPost(params.slug); // runs at BUILD time
return <Article post={post} />;
}
// ISR — same shape, but tell Next.js to regenerate in the background
export const revalidate = 60; // seconds
export default async function ProductPage({ params }) {
const product = await getProduct(params.id); // fresh at most every 60s
return <ProductView product={product} />;
}
// SSR — force fresh data on EVERY request (e.g. user-specific dashboards)
export const dynamic = 'force-dynamic';
export default async function Dashboard() {
const stats = await getLiveStats(); // runs on every request, on the server
return <StatsPanel stats={stats} />;
}
// CSR — a Client Component that fetches after mount, in the browser
'use client';
export default function LiveTicker() {
const [price, setPrice] = useState(null);
useEffect(() => {
const id = setInterval(() => fetch('/api/price').then(r => r.json()).then(setPrice), 1000);
return () => clearInterval(id);
}, []);
return <span>{price ?? 'Loading…'}</span>;
}Remember
Key Takeaways
- CSR: HTML built in the browser. SSG: built once at deploy. SSR: built every request. ISR: built at deploy, refreshed on a timer.
- Static strategies (SSG/ISR) are fastest and cheapest but trade freshness; SSR is freshest but costs per-request server compute.
- ISR gives stale-while-revalidate behavior: serve the cached page instantly, regenerate in the background after revalidate seconds.
- Hydration attaches interactivity to server-rendered HTML — mismatches between server and client output cause hydration errors.
- React Server Components let you choose server-only vs client per COMPONENT, not just per page — the current default in the Next.js App Router.
Do It
Practice
- 1Build the same page three ways in Next.js (SSG, ISR with revalidate=10, SSR with force-dynamic) and compare response times in the Network tab.
- 2Intentionally trigger a hydration mismatch (render Date.now() directly) and read the resulting React warning in the console.
- 3Explain, for your own portfolio site, which strategy each route type would use in a Next.js rewrite (home, blog post, live dashboard).