skip to content

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