skip to content

Choosing SSR, SSG, or ISR

The core decision question: does this page need per-request data, can it be built once, or does it just need to be fresh within a few minutes? Interviewers hand you a page description and expect a defended answer with tradeoffs.

part ofNext.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In a Next.js App Router app, what is the difference between a route that is prerendered at build time and one that is rendered on every request, in terms of when its data is read and what each visitor receives?

level: juniorimportance: must knowfreq 72%

answer

  1. one question: when does the component run
  2. build once for everyone, or once per visit
  3. data frozen at build vs read now
  4. static file vs server invocation per request
  5. freshness paid for in latency and cost

basics

~20 s

A prerendered route fetches its data once during the production build and serves the same stored HTML to every visitor. A per-request route re-runs that code on each visit, so each visitor gets markup built from data read at that moment.

solid answer

~50 s

Both routes can be written with exactly the same async Server Component code; what differs is *when* that code runs. A prerendered route runs at `next build`: its data is fetched once, the resulting HTML and RSC payload are written to disk, and every visitor afterwards is handed those same bytes, so the data is as old as the build. A per-request route runs the component on the server for each incoming request, so the data is fresh at that instant, but the visitor waits for whatever the render and its upstream calls cost. The practical consequences follow from that: prerendered output is a static asset that any cache or CDN can serve identically to everyone, while per-request output is unique to that request and cannot be shared blindly. Both still ship the same client JavaScript — the mode decides when HTML is produced, not whether the page is interactive.

code

tsx · 17 lines
tsx
// app/pricing/page.tsx
// Identical source; the rendering mode decides when this runs.
async function getPlans() {
  const res = await fetch('https://api.example.com/plans')
  return res.json()
}

export default async function PricingPage() {
  const plans = await getPlans()
  return (
    <ul>
      {plans.map((p: { id: string; name: string }) => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  )
}

go deeper

for a junior

Be ready to say plainly that the difference is when the component runs — once at build, or once per request — and that prerendered pages serve the same HTML to everyone.

for a middle

Explain the mechanics: what artifacts a build produces, why a prerendered response needs no server invocation, and how staleness arises from data frozen at build time.

for a senior

Show that you reason per route rather than per app, and connect the choice to real numbers: server cost per request, time to first byte, and how long stale content is tolerable for that page.

for a principal

Own the framing that rendering mode is a freshness-versus-cost contract per surface, and be able to argue when paying for per-request renders is genuinely worth it against the operational simplicity of static output.

## The one thing that actually differs In the App Router, a page is written as an async Server Component that fetches what it needs and returns JSX. That source code does not say when it runs. The rendering mode is the answer to exactly one question: **at what moment does Next.js execute this component?** There are two moments. - **Build time.** During `next build`, Next.js executes the component, collects the HTML plus the serialized RSC payload for the route, and writes them out as files. This is prerendering, historically called SSG (static site generation). - **Request time.** Next.js runs the component on the server once per incoming HTTP request and streams the result back. This is dynamic rendering, historically called SSR (server-side rendering). Everything else people say about "static vs dynamic" is a downstream consequence of that one difference. ## What the visitor receives With a prerendered route, every visitor receives byte-identical HTML. The tenth visitor and the ten-thousandth get the file produced at build. Nothing on the server needs to run to answer the request, so the response can come from a plain file server or a CDN edge, and the time to first byte is essentially network latency. With a per-request route, the visitor receives HTML that was produced for them. The server executed the component, awaited whatever it awaited, and sent the result. The response can reflect anything known about that request, and it costs a server invocation plus the latency of the slowest upstream call the component makes. ## When the data is read This is the part juniors most often get wrong. Prerendering does **not** mean "no data". A prerendered page may call a database or an API — it just calls it once, during the build, and the answer is frozen into the output: ```tsx // app/pricing/page.tsx export default async function Pricing() { const plans = await getPlans() // runs at build for a prerendered route, return <PlanTable plans={plans} /> // and per request for a dynamic one } ``` If `getPlans()` returns something different an hour after the build, a prerendered page keeps showing the old answer until the site is rebuilt or the route is configured to revalidate. That staleness is not a bug; it is the deal you accepted in exchange for a static file. ## What prerendering is not Two confusions are worth naming explicitly. **It is not client-side rendering.** A prerendered page still arrives as complete HTML — the content is in the document, which is why it is good for crawlers and for first paint. Client-side rendering ships an empty shell and fills it in the browser; that is a different thing entirely. **It is not "no JavaScript".** Any Client Components on the page are still bundled, still shipped, and still become interactive in the browser. A prerendered marketing page with an interactive pricing toggle is perfectly normal. Choosing a rendering mode decides when the HTML was manufactured, not whether the page has behaviour. ## Why the choice is per route, not per app Next.js decides this route by route — really segment by segment — rather than for the whole application. The same deployment can hold a prerendered marketing homepage, a per-request account dashboard, and article pages that sit somewhere in between. That is deliberate: freshness requirements differ per page, and forcing one mode across the app means either paying for server renders you did not need or shipping stale data where it matters. ## The tradeoff in one line Prerendering trades freshness for cheap, uniform, cacheable responses. Per-request rendering trades cost and latency for data that is correct at the instant of the request. Most real answers in an interview start by asking which of those two the page actually needs, and the honest answer is often "neither extreme" — which is what time-based revalidation exists to cover.

  • If the underlying data changes an hour after the build, what does a prerendered route show?
    The build-time data, indefinitely. A prerendered route has no idea the source changed; it keeps serving the stored HTML until something replaces it — a new deployment, a configured revalidation window, or an explicit invalidation. If an hour of staleness is unacceptable, prerendering with no revalidation is the wrong mode for that page.
  • Does a prerendered page still become interactive in the browser?
    Yes. Prerendering only fixes when the HTML was produced. Any Client Components on that page are bundled and shipped as usual, so buttons, menus and forms work exactly as they would on a per-request page. The choice affects freshness, cost and cacheability of the HTML — not whether the page has client behaviour.

saying these in an interview costs you the question

  • Thinks a prerendered page cannot contain any JavaScript or interactivity
  • Believes statically generated pages update themselves when the database changes
  • Confuses build-time prerendering with client-side rendering of an empty shell
  • Assumes the whole app must pick one rendering mode
  • Says per-request rendering means the data is fetched in the browser

context

open as a page

A Next.js App Router app has three pages: a marketing landing page, a news index that must be at most a minute out of date, and a signed-in account dashboard. Which rendering mode would you choose for each, and what drives each decision?

level: middleimportance: must knowfreq 78%

basics

~20 s

Prerender the landing page at build time, give the news index a short revalidation window so it regenerates roughly every minute, and render the dashboard per request because its output depends on who is asking. The driver is whether output varies per request, then how stale it may be.

open as a page

A Next.js App Router route is configured with a revalidate value of 60 seconds, and its cached page was generated 90 seconds ago. What does the next visitor receive, and when does fresh HTML first reach a user?

level: middleimportance: should knowfreq 62%

basics

~20 s

That visitor immediately receives the 90-second-old cached page. Their request marks the entry stale and triggers a regeneration in the background; once it finishes the cached copy is replaced, so a later visitor is the first to see fresh HTML.

open as a page

An e-commerce Next.js App Router app serves 50,000 product pages from a /products/[slug] route. How do you decide whether to prerender all of them at build time, and what are your options when a full build takes too long?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Prerender the whole catalogue only if the build stays within an acceptable deploy time and the slug set is known. Otherwise return just the high-traffic slugs from generateStaticParams and let the rest render on first request and be cached from then on.

open as a page

A team has made every route in their Next.js App Router app render on each request because it is simpler and always fresh. As the engineer leading the app, how do you evaluate that default and decide which routes to pull back to static or revalidated rendering?

level: principalimportance: should knowfreq 38%

basics

~20 s

Treat it as a cost and resilience decision, not a style preference. Inventory routes by whether output depends on the requester, their freshness tolerance and their traffic share, then convert the highest-traffic request-independent routes first and measure the change.

open as a page