Rendering strategies: SSR, SSG, ISR and client rendering compared
In short: A web page can be generated at build time (SSG), on every request (SSR), periodically anew (ISR) or in the browser (CSR); the choice affects speed, freshness, cost and search-engine suitability.
In more detail: The question is: when and where does the HTML arise? Frameworks such as Next.js let you choose this per page.
In Depth
The strategies
- SSG (static site generation): HTML is generated at build time and delivered via a CDN. Very fast, cheap, safe; but only as current as the last build. Ideal for blog, docs, wiki, landing pages.
- SSR (server-side rendering): the server generates the HTML on every request; always current and personalisable (cookies), but slower and more expensive (compute time, possibly database).
- ISR (incremental static regeneration): static page with expiry (
revalidate: 3600); after expiry it is rebuilt in the background. Good compromise for lists, shops, news. - CSR (client-side rendering): the browser loads JavaScript and builds the page; good for dashboards behind login, worse for first load time and SEO.
- Streaming/Suspense: the page is delivered in parts; important things appear first.
Decision aid
| Requirement | Choice |
|---|---|
| Rarely changing content, much traffic | SSG |
| Content changes every minute/hour | ISR |
| Different per user (cart, profile) | SSR or CSR |
| Behind login, interactive | CSR (or SSR + client components) |
| SEO important | SSG/ISR/SSR |
| Hybrids are normal: static shell, dynamic parts via streaming or client query. |
Effects
- Database load: SSR/ISR query it; with caching (see unstable_cache, revalidate) and long expiry times load falls sharply, especially with serverless databases that pause when idle and bill compute time.
- Cost and speed: CDN delivery of static pages is cheapest.
- Resilience: static pages keep running even if the database is unreachable; builds therefore must not depend on the database or must catch errors.
- Freshness: with on-demand revalidation (tags/paths) you can rebuild specifically after changes.
Practice
Start static, add dynamics where needed. Measure with Lighthouse/Core Web Vitals (LCP, CLS, INP). Set cache headers suitably. Test behaviour on database outage and cold starts. In Next.js: export const revalidate = 3600, dynamic = 'force-dynamic', generateStaticParams, server components with fetch and cache options (check the docs of your version, the API changes).
See also: Next.js App Router, Caching strategies, Cache tags and revalidateTag, Lighthouse and Core Web Vitals, Serverless