CodeOath
← All posts
Next.js31 min total · 12 parts

Next.js Fundamentals: The App Router, Server Components, and the Caching Layer Nobody Warns You About

Part 8 of 12 · ~2 min

Rendering Strategies

SSR, SSG, and ISR aren't three unrelated features — they're the same underlying question, answered at three different points in time: when does the HTML for this page actually get generated?

StrategyHTML generatedBest for
SSR (server-side rendering)Per request, fresh every timeData that's different for every user or changes on every load — a dashboard, an authenticated account page
SSG (static site generation)Once, at build timeContent that's identical for every visitor and rarely changes — a marketing page, docs
ISR (incremental static regeneration)At build time, then regenerated on a timer or on demandContent that's mostly static but does change occasionally — a blog post, a product listing

In the App Router, you don't pick one of these with a distinct named API the way the Pages Router required (getServerSideProps versus getStaticProps) — the same fetch-caching controls from the Data Fetching chapter are what determine which of these three a given page effectively becomes:

// Effectively SSR — no-store means this fetch (and the page around it)
// re-runs fresh on every single request
async function getDashboardData(userId: string) {
  const res = await fetch(`https://api.example.com/dashboard/${userId}`, { cache: "no-store" });
  return res.json();
}

// Effectively SSG — force-cache (the default) means this is fetched once
// and the resulting HTML is reused for every visitor after that
async function getMarketingCopy() {
  const res = await fetch("https://api.example.com/marketing-copy");
  return res.json();
}

// Effectively ISR — revalidate on a timer means the page is mostly static
// but never more than an hour stale
async function getBlogPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`, { next: { revalidate: 3600 } });
  return res.json();
}

Picking correctly is really about answering one question honestly for the page in front of you: is this HTML the same for everyone, and can it tolerate being a little stale?

  • A dashboard showing a specific user's account balance has to be correct right now, for that specific user — SSR (no-store), full stop. Serving anyone a cached version of someone else's dashboard isn't a performance trade-off, it's a data leak.
  • A marketing landing page is identical for every single visitor and changes only when someone on your team deploys new copy — SSG. There's no reason to regenerate the exact same HTML on every request when it's already sitting there, ready.
  • A blog post is the interesting middle case: the same for every reader, but it can change — a typo fix, an edit — without needing to be instant. ISR with a reasonable revalidation window (or on-demand revalidatePath the moment an edit is actually published) gets you the speed of static HTML with content that doesn't stay wrong indefinitely.