"SPA / SSR / SSG / RSC / ISR / PPR" — Next.js docs are an acronym storm. They're all different combinations of "where does the page get built" + "how much JS goes to the client". This guide covers each model's precise difference, the TTFB / LCP / interactivity trade-offs, and the truth that Next.js App Router isn't pure RSC.
Four (+ Two) Models by Criterion
Criterion 1 — where the HTML is built:
- Build time → SSG
- Server (per request) → SSR
- Client (browser) → SPA
- Server, per-component → RSC
Criterion 2 — what's in the JS bundle:
- All component code → SPA, SSR
- Client-only components → RSC
Criterion 3 — when the user can interact:
- After download + hydration → SSR, SSG
- After download → SPA
- Hydration ~0 → Qwik, Astro islands
Criterion 4 — where data is fetched:
- On the client (useEffect / TanStack Query) → SPA
- On the server (before page) → SSR, RSC
- At build → SSGSPA (Single Page Application)
Flow:
1. server → index.html (nearly empty) + bundle.js
2. browser → downloads bundle.js (~MB)
3. JS executes → React/Vue/Svelte builds DOM
4. useEffect → API fetch
5. data arrives → render
6. user interactive
TTFB: fast (small HTML)
LCP: slow (must wait for JS + fetch)
Interactive: slow
SEO: poor (crawlers without JS see empty HTML)
Examples: CRA, default Vite, React Router
Good for: internal dashboards, authenticated apps (SEO irrelevant)
Bad for: marketing sites, blogs, e-commerceSSR (Server-Side Rendering)
Flow:
1. user request → server renders React → HTML (incl. DB fetch)
2. browser → paints HTML instantly
3. JS bundle downloads
4. hydration → interactive
TTFB: medium (includes server render time)
LCP: fast (HTML paints instantly)
Interactive: medium (hydration cost)
SEO: good (HTML is complete)
Examples: Next.js Pages Router, Remix, Nuxt 3
Good for: fresh data per request (dashboards, feeds)
Bad for: global sites where TTFB matters (no CDN edge = server every time)SSG (Static Site Generation)
Flow:
Build time: render all pages → HTML files (deploy to CDN)
Runtime: CDN returns static HTML instantly
TTFB: very fast (CDN edge)
LCP: very fast
Interactive: medium (hydration)
SEO: excellent
Cost: nearly free (no server at idle)
Examples: Next.js generateStaticParams, Astro, Hugo, Gatsby
Good for: blogs, docs, marketing sites (low change frequency)
Bad for: frequently changing content (rebuild needed)
personalized content (can't have per-user HTML)ISR (Incremental Static Regeneration, Next.js)
Solves SSG's limits (rebuild time + change frequency):
- First: SSG (static HTML)
- After some time (revalidate=60), marked stale
- Next request → return stale immediately + regenerate in background
- Subsequent request → new HTML
→ "Up to 60s stale is OK" trade for SSG speed + SSR freshness.
→ Best fit for Wikipedia / news sites.RSC (React Server Components)
Flow:
1. user request → server builds React tree → RSC payload (text format)
2. server starts streaming response (HTML alongside)
3. browser paints as HTML arrives (fast TTFB)
4. JS bundle download — no server-component code (smaller)
5. hydration (client components only)
TTFB: fast (streaming)
LCP: fast
Interactive: fast (small JS)
SEO: excellent (HTML complete)
Bundle: small (excludes server-only code)
Examples: Next.js App Router, Remix v2+
Key novelty: smaller bundle + server logic directly in componentsPPR (Partial Prerendering, Next.js 14+)
Mix part-static, part-dynamic on the same page:
page:
<Header /> ← static (build time)
<UserGreeting /> ← dynamic (per request, per user)
<Footer /> ← static
Build: static parts pre-rendered → cached at CDN
Runtime: on request, stream static HTML immediately + server-render dynamic parts → stream
→ Near-zero TTFB (static starts immediately) + dynamic parts streamed.
→ Combines SSR personalization with SSG speed.
Status: as of 2026-05, Next.js experimental → moving toward stable.Trade-off Table
| Model | TTFB | LCP | TTI | Bundle | SEO | Cost |
|---|---|---|---|---|---|---|
| SPA | Fast | Slow | Slow | Large | Poor | Cheap (CDN only) |
| SSR | Medium | Fast | Medium | Large | Good | Expensive (server every time) |
| SSG | Very fast | Very fast | Medium | Large | Excellent | Cheap |
| ISR | Very fast | Very fast | Medium | Large | Excellent | Cheap + regenerate cost |
| RSC | Fast | Fast | Fast | Small ✓ | Excellent | Medium (server logic) |
| PPR | Very fast | Very fast | Fast | Small | Excellent | Medium |
Next.js App Router Isn't Pure RSC
The Next.js App Router defaults to RSC — but it's actually:
- server components (RSC) — code doesn't reach client
- client components ("use client") — SPA-like hydration
- SSR — client components' first render also on the server
- SSG — paths decided as static are built at build time
- ISR — pages with revalidate
- PPR — partial prerendering (experimental)
→ Next.js App Router = a hybrid of RSC + SSR + SSG + ISR + PPR.
Mode is set differently per page / component.
This hybrid is the modern answer:
- Server component by default (smaller bundle)
- Client only for dynamic parts
- SSG for infrequent updates, ISR for medium, SSR for per-request
- PPR mixes static + dynamic streamingCommon Pitfalls
- "We're SSR" oversimplification — reality is mixed. State each route / component's mode.
- SSG everything — heavy rebuild burden for frequently-changing data. ISR or SSR.
- SPA with SEO ignored — marketing / blog as SPA = hard to surface in search. SSR or SSG.
- SSR everything — server cost ↑ + can't leverage edge cache. Consider SSG / ISR.
- RSC "use client" too high — at page top, RSC benefit = 0. Push toward leaves.
Wrap-up
SPA / SSR / SSG / RSC differ on "where HTML" + "how much JS to the client". Modern Next.js App Router is a hybrid of 6 modes — chosen per page / component.
Practical picks: - Blogs / docs / marketing = SSG (or ISR) - Frequently-changing dashboards = SSR (or RSC) - Global, personalized = PPR + edge - Authenticated internal apps (no SEO) = SPA