SSG, SSR, ISR & PPR

Next.js supports several rendering strategies, chosen per route to balance speed and freshness.

Next.js is not tied to one rendering model. Each route can use whichever strategy fits its data:

StrategyWhen it rendersBest for
Static (SSG)At build time, reusedMarketing, docs
Dynamic (SSR)On every requestPersonalized, live data
ISRCached, rebuilt on a timerBlogs, catalogs
PPRStatic shell + dynamic holesMixed pages

Partial Prerendering (PPR) is the newest: it serves a static shell instantly and streams in the dynamic parts, giving you the best of both.

Example

Example · typescript
// Static by default (no dynamic APIs, cached fetches)
// -> SSG
export default async function Page() {
  const data = await fetch('https://api.example.com/docs', {
    cache: 'force-cache',
  }).then((r) => r.json());
  return <article>{data.title}</article>;
}

When to use it

  • A marketing landing page uses SSG to pre-render at build time so every visitor gets HTML from the CDN edge instantly.
  • A personalised dashboard uses SSR to render fresh, user-specific data on every request without a client-side fetch.
  • An e-commerce product page uses ISR with a 5-minute revalidation so inventory changes propagate quickly without rebuilding the whole site.

More examples

Static generation (SSG)

cache: 'force-cache' (the default) causes Next.js to render this page once at build time and serve the result statically.

Example · ts
// app/about/page.tsx — statically generated at build time
export default async function AboutPage() {
  const content = await fetch('https://cms.example.com/about', {
    cache: 'force-cache',
  }).then(r => r.json());
  return <section>{content.body}</section>;
}

Server-side rendering (SSR)

Reading cookies() automatically opts this route into dynamic SSR so data is always personalised and fresh.

Example · ts
// app/orders/page.tsx — rendered on every request
import { cookies } from 'next/headers';

export default async function OrdersPage() {
  const cookieStore = await cookies();
  const token = cookieStore.get('auth-token')?.value;
  const orders = await fetchOrders(token);
  return <ul>{orders.map((o: any) => <li key={o.id}>{o.total}</li>)}</ul>;
}

ISR with revalidate

Exporting revalidate makes Next.js serve the cached page and regenerate it in the background after the interval.

Example · ts
// app/products/page.tsx — ISR: rebuild every 5 minutes
export const revalidate = 300;

export default async function ProductsPage() {
  const products = await fetch('https://api.example.com/products').then(r => r.json());
  return <ul>{products.map((p: any) => <li key={p.id}>{p.name}</li>)}</ul>;
}

Discussion

  • Be the first to comment on this lesson.