~/TechPurAI
~/tutorials/nextjs-from-scratch/rendering-strategies
intermediate·part 17 of 22·3 min read

Rendering strategies: SSG, SSR, and ISR

Updated Aug 16, 2026JavaScript · Next.js

Every page in this series has been rendering one of three ways without it ever being named directly. This part names them, and covers how to deliberately choose between them — the choice affects real production behavior, not just a config detail.

Static Site Generation (SSG): the default

app/posts/[slug]/page.tsx, with generateStaticParams from part 6, is SSG: every post's HTML gets generated once, at next build time, and served identically to every visitor from a CDN with no per-request server work at all. This is the fastest possible option, and the App Router's default whenever a route has no dynamic, per-request data source.

Server-Side Rendering (SSR): fresh on every request

tsx
// app/posts/[slug]/page.tsx
export const dynamic = "force-dynamic";

Adding this forces the page to render fresh on every single request instead — the right call for something that's genuinely different per visit (a personalized dashboard, live data that can't tolerate any staleness). It's also strictly slower than SSG for every visitor, since there's no pre-built HTML to serve directly — worth reaching for deliberately, not as a default.

Incremental Static Regeneration (ISR): the middle ground

tsx
// app/posts/page.tsx
export const revalidate = 60;

This is what part 5's fetch(..., { next: { revalidate: 3600 } }) was already doing at the level of one specific fetch call — export const revalidate applies the same idea to an entire page. The first visitor after 60 seconds have passed still gets the previous, cached HTML instantly, but Next.js regenerates it in the background for that request — so the next visitor after that gets the fresh version, without anyone ever waiting on a live render.

Choosing between them for the blog

Revalidating on demand instead of on a timer

ts
// app/actions.ts
import { revalidatePath } from "next/cache";

export async function publishPost(slug: string) {
  // ... save the post ...
  revalidatePath(`/posts/${slug}`);
  revalidatePath("/posts");
}

This is the same revalidatePath from part 14 — worth re-reading in this context specifically: it's ISR triggered by an actual event (publishing a post) instead of waiting for the next scheduled revalidate window to pass. A blog with an admin action to publish a post should call this immediately rather than waiting up to a full revalidate period for the new post to actually appear.

Common mistake

Reaching for force-dynamic on every page "to be safe" against stale data, without checking whether the page's content genuinely changes per-request. Most content — including most of what a blog serves — doesn't need SSR's per-request cost; ISR with a reasonable revalidate window covers "eventually fresh, but fast for every visitor" far better for content that changes occasionally rather than constantly.

Next: middleware — code that runs before a request even reaches a page, for things like redirects that shouldn't require rendering anything first.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous16. Environment variables and secretsnext →18. Middleware: redirects and edge logic