---
title: "06 — Rendering Strategies — When and Where the HTML Is Made"
uid: rendering-strategies
tags: ["roadmap:nextjs", "ssg", "ssr", "csr", "rendering", "spa", "nextjs"]
excerpt: "Every rendering strategy answers one question: when, and on which machine, does the HTML get generated — build time, request time, or in the browser? SSR, SSG, ISR, CSR are labels on that."
date: 2026-08-13T03:28:02+0000
source: https://www.aveshina.my.id/en/blog/rendering-strategies
---

The conceptual heart of Next.js flattened into one question for me. The model that finally tamed the acronym soup: **every rendering strategy is just an answer to one question — when, and on which machine, does the HTML get generated?** [1][2] The options are build time, request time, or in the browser. Everything else — SSR, SSG, CSR, SPA, ISR — is a label for a specific combination of those.

Once I stopped memorizing the acronyms and started asking "build, request, or browser?" the whole framework's rendering model became navigable.

## The decision, on one axis

The roadmap's rendering-strategies node frames it cleanly: different strategies trade off performance, SEO, and data freshness, and choosing the right one depends on each page's content [1]. Here's the same trade-off as a timeline:

```figure
<svg viewBox="0 0 740 320" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Rendering strategies on a timeline of when HTML is generated. Three points: build time (SSG — HTML made once, cached forever), request time (SSR — HTML made per request, fresh but slower), and browser (CSR/SPA — server sends shell, browser renders). ISR sits between SSG and SSR, regenerating periodically.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- timeline -->
    <line x1="60" y1="160" x2="680" y2="160" stroke="#64748b" stroke-width="2"/>
    <polygon points="680,160 672,156 672,164" fill="#64748b"/>
    <text x="690" y="164" font-size="11" fill="#64748b">time →</text>

    <!-- markers -->
    <circle cx="120" cy="160" r="10" fill="#dcfce7" stroke="#16a34a" stroke-width="2"/>
    <circle cx="370" cy="160" r="10" fill="#fef9c3" stroke="#ca8a04" stroke-width="2"/>
    <circle cx="620" cy="160" r="10" fill="#fee2e2" stroke="#dc2626" stroke-width="2"/>

    <!-- SSG -->
    <rect x="50" y="40" width="180" height="100" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="140" y="62" font-size="13" font-weight="700" fill="#052e16" text-anchor="middle">SSG</text>
    <text x="140" y="80" font-size="10" fill="#052e16" text-anchor="middle">build time</text>
    <text x="140" y="100" font-size="10" fill="#052e16" text-anchor="middle">fastest, cacheable</text>
    <text x="140" y="118" font-size="10" fill="#052e16" text-anchor="middle">stale until rebuild</text>
    <line x1="140" y1="140" x2="140" y2="150" stroke="#16a34a" stroke-width="1.5"/>

    <!-- SSR -->
    <rect x="280" y="40" width="180" height="100" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="370" y="62" font-size="13" font-weight="700" fill="#422006" text-anchor="middle">SSR</text>
    <text x="370" y="80" font-size="10" fill="#422006" text-anchor="middle">request time</text>
    <text x="370" y="100" font-size="10" fill="#422006" text-anchor="middle">always fresh</text>
    <text x="370" y="118" font-size="10" fill="#422006" text-anchor="middle">server work per hit</text>
    <line x1="370" y1="140" x2="370" y2="150" stroke="#ca8a04" stroke-width="1.5"/>

    <!-- CSR/SPA -->
    <rect x="530" y="40" width="160" height="100" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="610" y="62" font-size="13" font-weight="700" fill="#7f1d1d" text-anchor="middle">CSR / SPA</text>
    <text x="610" y="80" font-size="10" fill="#7f1d1d" text-anchor="middle">in the browser</text>
    <text x="610" y="100" font-size="10" fill="#7f1d1d" text-anchor="middle">fluid after load</text>
    <text x="610" y="118" font-size="10" fill="#7f1d1d" text-anchor="middle">slow first paint</text>
    <line x1="610" y1="140" x2="610" y2="150" stroke="#dc2626" stroke-width="1.5"/>

    <!-- ISR (between SSG and SSR) -->
    <circle cx="245" cy="160" r="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="2"/>
    <rect x="175" y="200" width="160" height="80" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="255" y="222" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">ISR</text>
    <text x="255" y="240" font-size="10" fill="#1e1b4b" text-anchor="middle">static, regenerates</text>
    <text x="255" y="258" font-size="10" fill="#1e1b4b" text-anchor="middle">on a interval</text>
    <line x1="245" y1="200" x2="245" y2="168" stroke="#6366f1" stroke-width="1.5"/>

    <text x="370" y="300" font-size="11" font-style="italic" fill="#64748b" text-anchor="middle">the question is always: build, request, or browser?</text>
  </g>
</svg>
```

## Static Site Generation (SSG)

SSG generates the HTML once, at **build time** [5]. When a user requests the page, the server hands back a pre-rendered file — no runtime work. This is the fastest possible strategy (CDN-cacheable, instant) and gives perfect SEO, at the cost of freshness: content goes stale until the next build. SSG is the right call for content that rarely changes — marketing pages, documentation, blog posts published in batches.

## Server-Side Rendering (SSR)

SSR generates the HTML on the server **per request** [2][3]. The page is always fresh, SEO is good because real HTML reaches the crawler, but the server does work on every hit. SSR suits personalized or frequently-updated content where staleness is unacceptable — a logged-in dashboard, real-time data.

## Client-Side Rendering (CSR) and SPA

CSR sends a minimal HTML shell from the server; the browser downloads JavaScript and renders the UI itself [4]. A SPA takes this to its conclusion — one HTML page, all rendering in-browser, navigation without reloads [3]. The trade-off: fluid UX after the initial load, but a slow first paint (nothing meaningful renders until JS runs) and poor SEO (the crawler sees an empty shell). CSR is the choice for highly interactive apps behind auth, where SEO doesn't matter and the initial load is a one-time cost.

## Incremental Static Regeneration (ISR)

ISR is the hybrid that makes SSG viable for content that updates: the page is statically generated, but Next.js regenerates it in the background at a configurable interval or on-demand. Users see the cached version instantly; the next request after the interval triggers a fresh build. ISR is what lets a blog or news site stay mostly-static without rebuilding on every publish — the best of SSG's speed with something close to SSR's freshness.

## The App Router's rendering model

In the App Router, the default is **Server Components**, which render on the server [6]. The framework decides per route whether it can be statically prerendered (no request-time data) or must render dynamically (uses request-time data like cookies or search params). This is sometimes called "static-first with dynamic when needed" — the compiler figures out the strategy from the code, rather than me declaring it per page. Partial Prerendering pushes this further by letting a single route mix a static shell with dynamic holes streamed in via Suspense, though it remains experimental [6].

## Choosing per page

The whole point is that no single strategy is correct for an entire app. A typical Next.js site combines them:

- Marketing home → **SSG** (static, fast, SEO-critical).
- Blog post → **SSG** with **ISR** (static, but regenerates on edit).
- User dashboard → **SSR** (personalized, always fresh).
- In-app editor → **CSR** (highly interactive, behind auth).

## How I use this

For every route I build, I ask the one question — *when should this HTML be generated?* If the content is the same for everyone and rarely changes, SSG. If it updates on a cadence, ISR. If it's personalized or real-time, SSR. If it's a behind-auth interactive surface, CSR. The framework lets me mix these per route, which is precisely its value over committing to one strategy globally.

## References

[1] Vercel, "Rendering strategies," Next.js Learn, 2024. [Online]. Available: [https://nextjs.org/learn/seo/rendering-strategies](https://nextjs.org/learn/seo/rendering-strategies)

[2] Vercel, "Server-side rendering (SSR)," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering](https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering)

[3] "SSR vs. SPA showdown: choosing the right rendering approach for your web app," dev.to, 2024. [Online]. Available: [https://dev.to/santhanam87/ssr-vs-spa-showdown-choosing-the-right-rendering-approach-for-your-web-app-4439](https://dev.to/santhanam87/ssr-vs-spa-showdown-choosing-the-right-rendering-approach-for-your-web-app-4439)

[4] Vercel, "Client-side rendering (CSR)," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/pages/building-your-application/rendering/client-side-rendering](https://nextjs.org/docs/pages/building-your-application/rendering/client-side-rendering)

[5] Vercel, "Static site generation (SSG)," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation](https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation)

[6] Vercel, "Partial prerendering," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/getting-started/partial-prerendering](https://nextjs.org/docs/app/getting-started/partial-prerendering)

[7] HybridHeroes, "Next.js rendering strategies: SSR, SSG, and ISR compared," 2023. [Online]. Available: [https://hybridheroes.de/blog/2023-05-31-next-js-rendering-strategies/](https://hybridheroes.de/blog/2023-05-31-next-js-rendering-strategies/)

```quiz
Q: SSG generates HTML at…
- build time
- request time
correct: 0
explain: Static Site Generation produces HTML once at build time. The server hands back a pre-rendered file per request — fastest possible, but content is stale until the next build.

Q: Which strategy best fits a personalized logged-in dashboard with always-fresh data?
- SSG
- SSR
correct: 1
explain: SSR generates HTML per request on the server, so the page is always fresh and personalized. SSG would be stale; CSR would lose the fast first paint.

Q: What does ISR add on top of SSG?
- Client-side hydration
- Background regeneration on an interval or on-demand, so static pages refresh without a full rebuild
correct: 1
explain: Incremental Static Regeneration serves the cached static page instantly, then regenerates it in the background after a configurable interval — SSG's speed with closer-to-SSR freshness.

Q: Why is CSR/SPA's first paint typically slow?
- The server is slow to generate HTML
- The browser must download, parse, and run JavaScript before meaningful content appears
correct: 1
explain: CSR sends a minimal HTML shell; the actual UI renders only after the JavaScript bundle loads and executes. The initial paint is delayed by that JS cost.

Q: In the App Router, what determines whether a route is statically prerendered or rendered dynamically?
- A per-page flag you set manually
- The compiler infers it from whether the route uses request-time data (cookies, search params, headers)
correct: 1
explain: The App Router is static-first: if a route reads no request-time data, it's prerendered statically; if it does, it renders dynamically. The strategy is inferred from the code.
```
