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?
| Strategy | HTML generated | Best for |
|---|---|---|
| SSR (server-side rendering) | Per request, fresh every time | Data that's different for every user or changes on every load — a dashboard, an authenticated account page |
| SSG (static site generation) | Once, at build time | Content 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 demand | Content 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?
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.revalidatePath the moment an edit is actually published) gets you the speed of static HTML with content that doesn't stay wrong indefinitely.