SSG SSR and ISR Compared

Side by Side

SSGISRSSR
HTML builtAt buildAt build + in background after N sPer request
Data freshnessBuild timeAt most N seconds old (or on-demand)Always fresh
TTFBBest (CDN)Best (CDN)Depends on data
Server costLowestLowHighest
Personalised contentNoNoYes
App Router triggerDefault for routes without dynamic APIsrevalidateDynamic APIs / no-store / force-dynamic

Pick Per Route

Example

A rule of thumb as code. Ask: does it depend on the user? how often does the data change?

type Route = { path: string; perUser: boolean; changesEveryMin: number | null };

function strategy(r: Route): string {
  if (r.perUser) return 'SSR (dynamic)';
  if (r.changesEveryMin === null) return 'SSG (static)';
  return 'ISR (revalidate = ' + r.changesEveryMin * 60 + 's)';
}

const routes: Route[] = [
  { path: '/about', perUser: false, changesEveryMin: null },
  { path: '/blog/[slug]', perUser: false, changesEveryMin: null },
  { path: '/products/[id]', perUser: false, changesEveryMin: 60 },
  { path: '/deals', perUser: false, changesEveryMin: 5 },
  { path: '/cart', perUser: true, changesEveryMin: 0 },
  { path: '/account/orders', perUser: true, changesEveryMin: 0 },
];
for (const r of routes) console.log(r.path.padEnd(16), strategy(r));

Output

/about           SSG (static)
/blog/[slug]     SSG (static)
/products/[id]   ISR (revalidate = 3600s)
/deals           ISR (revalidate = 300s)
/cart            SSR (dynamic)
/account/orders  SSR (dynamic)