SSG vs SSR vs ISR in Next.js: How I Actually Decide
Jul 9, 2026 · 2 min read

"Should this be SSG or SSR?" comes up on basically every Next.js project. The honest answer is that it depends on how often the data changes and who needs to see the change first — not on which one is "faster" in the abstract.
The three options, briefly
- SSG (Static Site Generation): HTML is built once, at build time (or on-demand the first time a path is hit), and served from cache after that. Fastest possible response — there's no server work happening per request.
- SSR (Server-Side Rendering): HTML is generated fresh on every request. Necessary when the response has to reflect something request-specific — a logged-in user's data, for example.
- ISR (Incremental Static Regeneration): SSG with a timer. Pages are static, but Next.js will regenerate them in the background after N seconds have passed since the last render.
Where I landed on this exact site
This portfolio's project and blog pages are pure SSG with no time-based revalidation at all — not even ISR's timer. Every project and blog page is generated at build time via generateStaticParams, and it stays exactly as-is until something actually changes.
export async function generateStaticParams() {
const { data } = await supabase.from('projects').select('slug')
return data.map((p) => ({ slug: p.slug }))
}
The reason this works without ISR's timer: the only way content changes here is through the admin panel, and every create/update/delete action calls revalidatePath() for exactly the pages affected — the home page, the index page, the specific slug, and the old slug too if something got renamed (so the stale URL 404s instead of quietly lingering).
function revalidateProjectPages(slug, previousSlug) {
revalidatePath('/')
revalidatePath('/projects')
if (slug) revalidatePath(`/projects/${slug}`)
if (previousSlug && previousSlug !== slug) {
revalidatePath(`/projects/${previousSlug}`)
}
}
That's the key insight ISR's timer-based revalidation doesn't give you by default: on-demand revalidation lets you invalidate exactly what changed, the moment it changes — no stale window to wait out, and no wasted background regeneration of pages nothing touched.
When I'd reach for each one
- SSG + on-demand revalidation (what this site uses): content that changes through a known set of actions (an admin panel, a webhook, a CMS save). You always know exactly which paths to invalidate.
- ISR with a timer: content that changes from a source you don't control or can't hook into directly — a third-party API you're polling, for instance, where "eventually fresh, and cheap to serve" is good enough.
- SSR: anything genuinely per-request — authenticated dashboards, personalized content, anything where two different visitors hitting the same URL should see different HTML.
The mistake I see most often is reaching for SSR by default "to be safe," when most content on most sites changes far less often than a server render implies. Start from "how does this data actually change," not from "which one sounds more real-time."