Static vs dynamic: how I decide
The architecture decision that shapes every project. A simple framework for choosing between static generation, server rendering, and client-side fetching.
Every page on a site has a question underneath it: when does this data need to be fresh? The answer to that question determines the rendering strategy. Get it right and performance is almost free. Get it wrong and you're fighting it for the rest of the project.
Static generation: the default
If the content doesn't change between requests, render it at build time. Marketing pages, blog posts, product listings that update a few times a day: all of these belong in static generation.
// Next.js App Router: this page renders at build time
export default async function BlogPost({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
return <Article post={post} />;
}
// No export const dynamic, defaults to staticStatic pages are served from a CDN, load instantly, and cost almost nothing to host. There's no reason not to use this for content that isn't truly real-time.
ISR: static with a freshness window
Incremental static regeneration is static generation with an expiry. The page is cached and served statically, but after a set interval the next request triggers a background regeneration:
export const revalidate = 300; // rebuild at most every 5 minutesThis is the right pattern for product catalogues, pricing pages, and anything that changes throughout the day but doesn't need to be up-to-the-second. You get the performance of static with content that stays reasonably fresh.
Server rendering: when freshness is the feature
A dashboard showing live data. A page personalised by the user's account. A search results page that depends on the query. These need server rendering. The data must be fetched on every request:
export const dynamic = "force-dynamic";
export default async function Dashboard() {
const stats = await getUserStats(); // fetched on every request
return <StatsPanel stats={stats} />;
}Server rendering is slower than static by definition: you're doing work on each request. Use it only where the freshness justifies the cost.
Client-side fetching: interactions and real-time
For data that changes based on user interaction (a live search, a cart update, a notification count), client-side fetching is the right tool. The initial page can still be statically rendered; the dynamic parts hydrate and fetch independently.
"use client";
function NotificationBadge() {
const { data } = useSWR("/api/notifications", fetcher, { refreshInterval: 30000 });
return <span>{data?.count ?? 0}</span>;
}The decision in one question
How often does this specific data change, and does it need to be different for each user?
Never changes → static. Changes occasionally → ISR. Changes per-request but not per-user → server render. Changes based on user action → client fetch.
Most pages on most sites answer "never" or "occasionally". The default should be static. Server rendering is for the exceptions, and client-side fetching is for the interactions that make the page feel alive.
Got a project in mind?
I build fast, production-grade sites for freelance clients. Let’s talk.