Jun 2026 · Guide · ~10 min read
SSR vs SSG vs ISR in Next.js — Plain English Explanation
By Safdar Ali — frontend engineer, Bengaluru
I'm Safdar Ali. For six months I treated every Next.js page like SSR because "server" sounded safest. Marketing pages were slower than they needed to be; dashboard pages were over-cached. SSR vs SSG in Next.js is not a popularity contest — it is a delivery choice: when HTML is built, how often it refreshes, and who pays the compute bill. This article is the plain-English map I wish I had, with code for all three modes and a flowchart you can screenshot.
Three acronyms, one question: when is HTML built?
SSR (Server-Side Rendering) builds HTML on each request (or per-request cache miss). Fresh data, higher server load.
SSG (Static Site Generation) builds HTML at deploy time. Fastest CDN delivery; stale until you redeploy unless you add revalidation.
ISR (Incremental Static Regeneration) is SSG plus background refresh — static speed with a TTL. In App Router this is mostly fetch with next.revalidate.
// Mental model — same page, three delivery timings
// SSG: HTML built at deploy ──────────────────► CDN serves file
// ISR: HTML built at deploy ──► stale ──► regen in background after N sec
// SSR: HTML built when user hits URL ─────────► server sends fresh HTMLNone of these replace React. They describe when your React tree becomes HTML the browser can paint before hydration.
Decision flowchart — which mode to pick
START: New Next.js page
|
v
Does Google need HTML
with real content on
first request?
/ \
NO YES
| |
v v
Authenticated Is data identical
dashboard / SPA? for all users?
| / \
YES NO YES
| | |
v v v
"use client" + SSR or Changes
client fetch per-user every hour+
(no SSR win) data? / \
/ \ NO YES
YES NO | |
| | v v
v v SSG ISR
SSR ISR (rare) (revalidate)
or SSG
+ short
revalidatePrint that flowchart. When you are unsure, default marketing to ISR with a sensible revalidate, default logged-in dashboards to client rendering.
SSG — build once, serve from the edge
Use SSG for pages where data changes only when you deploy — legal pages, about pages, rarely updated landing heroes.
// app/about/page.tsx — static by default (no fetch cache opts)
export default function AboutPage() {
return (
<main>
<h1>About Safdar Ali</h1>
<p>Frontend engineer, Bengaluru — available for React / Next.js work.</p>
</main>
);
}
// Dynamic routes can still be SSG if you export generateStaticParams
export async function generateStaticParams() {
const slugs = await getAllBlogSlugs();
return slugs.map((slug) => ({ slug }));
}Tradeoff: a price change on an e-commerce SKU list will not appear until redeploy or ISR kicks in. For truly static content, that is a feature — zero origin load.
ISR — static speed, controlled freshness
// app/products/page.tsx — ISR via fetch revalidate (App Router)
type Product = { id: string; name: string; price: number };
async function getProducts(): Promise<Product[]> {
const res = await fetch("https://api.example.com/products", {
next: { revalidate: 3600 }, // ISR: refresh at most every hour
});
if (!res.ok) throw new Error("Failed to load products");
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name} — ₹{p.price}</li>
))}
</ul>
);
}First visitor after TTL might still see stale HTML while regeneration runs — acceptable for catalog pages, unacceptable for live stock tickers. Know your freshness requirement before picking ISR.
SSR — per-request HTML when data must be fresh
// app/dashboard/summary/page.tsx — SSR: no cache on fetch
async function getSummary(userId: string) {
const res = await fetch("https://api.example.com/summary/" + userId, {
cache: "no-store", // SSR — always fresh for this user
});
return res.json();
}
export default async function SummaryPage() {
const session = await getSession();
const summary = await getSummary(session.userId);
return <SummaryClient data={summary} />;
}SSR costs more at scale. Use it when personalisation or real-time correctness beats CDN economics — not because "server sounds professional."
SSR vs SSG vs ISR — comparison table
| Criteria | SSG | ISR | SSR |
|---|---|---|---|
| When HTML is built | Deploy time | Deploy + periodic regen | Each request |
| TTFB on CDN | Fastest | Fast (often cached) | Slower |
| Data freshness | Stale until deploy | TTL-based | Real-time |
| Server cost | Lowest | Low–medium | Highest |
| SEO | Excellent | Excellent | Excellent |
| Per-user data | Poor fit | Poor fit | Good fit |
| App Router signal | Default static page | revalidate: N | cache: no-store |
Marketing site vs dashboard — what I actually ship
Marketing site (public, SEO, shared content): ISR for blog and product listings, SSG for about/legal, Server Components for HTML. On a client rebuild I moved the blog to revalidate: 86400 and saw origin requests drop 70% — part of the story in my Next.js performance case study.
Dashboard (authenticated, personal): client components, SWR or WebSockets, no ISR fantasy. Trying to ISR user-specific charts is how teams waste a sprint.
// BEFORE — marketing page forced to SSR "just because"
export default async function PricingPage() {
const plans = await fetch("https://api.example.com/plans", { cache: "no-store" });
// Every visitor hits origin — same JSON for everyone
}
// AFTER — ISR: shared plans, CDN-friendly
export default async function PricingPage() {
const res = await fetch("https://api.example.com/plans", { next: { revalidate: 3600 } });
const plans = await res.json();
// ...
}My production setup
In production I label routes in file comments — ISR 1h or SSR session — so the next developer does not "optimise" a dashboard into ISR by accident. Pair this with RSC vs client components — rendering mode and component boundary are two separate decisions.
At my day job, the mistake I see most is SSR everywhere because the team learned Pages Router getServerSideProps first. App Router caching is more granular — use it.
The single takeaway
Pick rendering by freshness and audience, not acronym prestige. Marketing: ISR + SSG. Dashboards: client. Personalised SSR only when you mean it.
Related: App Router complete beginner guide. Contact.
If this helped you
I publish free tutorials and write-ups like this in my spare time — no paywall on the guides. If it saved you an afternoon of trial and error, you can support the work:
- Buy me a coffee at buymeacoffee.com/safdarali
- Subscribe to my YouTube channel — it's free; 70+ React & Next.js tutorials
Related reading
More guides on safdarali.in — same author, production-focused.
- Guide
How to Build a Frontend Developer Portfolio That Stands Out
Frontend developer portfolio guide for India — sections, React/Next.js examples, SEO, performance, personal branding, FAQ, and checklist to build and rank.
May 2026Read article →
- Guide
React Server Components vs Client Components — When to Use Which
Practical RSC vs client guide for Next.js App Router — when to use each, real code, bundle before/after, and performance impact.
May 2026Read article →