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?
answer
- two questions, in order
- request-dependent first, freshness second
- identical for everyone means cacheable
- staleness measured in minutes, not vibes
- never cache a signed-in page
basics
~20 sPrerender 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.
solid answer
~50 sI decide with two questions in order. First: does the output depend on this specific request — the signed-in user, a cookie, a search param? If yes, the route must render per request, so the dashboard is dynamic and must never be served from a shared cache. If no, the second question is how stale the page may be. The landing page changes only when someone deploys marketing copy, so prerender it at build and serve a static file. The news index changes constantly but tolerates about a minute of lag, so prerender it with a revalidation window: visitors get a cached page instantly and Next regenerates it in the background once the window lapses. That is the whole framework — *request-dependent* forces dynamic, and among the request-independent pages, the freshness tolerance picks between a plain build-time render and a revalidated one.
go deeper
Know the three shapes by example: a page that is the same for everyone and rarely changes, one that is the same for everyone but changes often, and one that differs per signed-in user.
Walk the decision out loud — request-dependence first, then freshness tolerance — and state what each choice costs in latency, server work and staleness rather than just naming the mode.
Anchor the answer in traffic and failure modes: which route absorbs a campaign spike, what still serves when the upstream API is down, and how you would verify the mode each route actually got.
Frame rendering mode as a per-surface freshness contract the team can state in seconds, and be ready to defend when paying for per-request rendering is worth the loss of cacheability.
## The decision procedure Interviewers hand you page descriptions because they want the reasoning, not a recital of three mode names. A defensible answer runs a short decision procedure and says out loud what each step rules out. **Question 1 — does the output depend on this request?** If the HTML must differ per user, per cookie, per geo, or per search parameter, no amount of caching helps: there is no single correct document to store. That route renders per request. This is the only hard constraint in the list; the rest are tradeoffs. **Question 2 — how stale may it be?** For request-independent pages, staleness is a product decision measured in time. "Only changes at deploy" points at a plain build-time prerender. "Changes constantly but a minute of lag is fine" points at a prerender with a revalidation window. "Must be correct to the second" pushes you back to per-request rendering even though the page is the same for everybody. **Question 3 — does the URL space fit in a build?** A route with a bounded, known set of URLs can be fully prerendered. A route with a very large or unknowable set has to prerender a subset, or nothing, and fill in the rest later. ## Applying it to the three pages ### Marketing landing page Same for every visitor, changes when marketing ships new copy, and it is the page most likely to be hit by a traffic spike from a campaign. Prerender it at build. The output is a file, so a spike costs you nothing, the page survives an outage of whatever CMS produced it, and time to first byte is delivery latency. The cost is that a copy change requires a rebuild or an explicit invalidation — acceptable, because copy changes are already tied to a deploy workflow. ### News index Also identical for every visitor — everyone sees the same headline list — so it is not a dynamic page. But it changes many times an hour, and rebuilding the site per story is absurd. This is exactly what incremental regeneration exists for: prerender it, attach a revalidation window of about 60 seconds, and let the first request after the window expires trigger a background regeneration while the visitor still gets an instant response. You keep the static-file economics and bound your staleness to roughly the window length. ```tsx // app/news/page.tsx export const revalidate = 60 // regenerate at most about once a minute export default async function NewsIndex() { const stories = await getTopStories() return <StoryList stories={stories} /> } ``` ### Account dashboard The content is the signed-in user's own data. There is no shared document to cache, and caching one by accident is a security incident, not a performance bug. Render per request. Accept the server cost and the upstream latency, and if the page feels slow, the fix is not a different mode — it is streaming the slow parts so the shell arrives early, or moving work off the critical path. ## Saying the tradeoffs, not just the verdicts What separates a middle answer from a junior one is naming what each choice costs. - **Build-time prerender**: cheapest per request, best time to first byte, immune to upstream outages at serve time — but the data ages until a rebuild, and the build itself grows with the number of pages. - **Revalidated prerender**: nearly the same economics, with staleness bounded by the window instead of by the deploy cadence — but somebody is always seeing a page up to one window old, and you must decide whether that is acceptable for this content. - **Per-request render**: always correct, can personalize — but every visit costs a server invocation, your time to first byte inherits the slowest upstream call, and a traffic spike hits your backend directly rather than a cache. ## Common traps The two failure modes are symmetrical. Making everything dynamic "to be safe" throws away the cheapest, most resilient tier for pages that never needed it. Making something static that is actually request-dependent is worse: it risks one user's page being served to another. When in doubt, prove the page is identical for all visitors before you make it static, and prove staleness is intolerable before you make it dynamic. Finally, note that the choice is per route. A single app happily mixes all three, and a good answer says so explicitly rather than picking one mode for the application.
- The product owner says the news index must never be more than five seconds stale. What changes?A five-second window means almost every request triggers a regeneration, so you get the cost of dynamic rendering with the confusion of a cache. At that tolerance I would render per request and put effort into making the query fast, or keep a short window only if traffic is high enough that many requests still share each generated copy.
- Where would streaming fit into these three pages?It is orthogonal to the mode choice. Streaming helps a page that renders per request and has one slow region: the shell reaches the browser immediately and the slow part fills in. It does not make a page fresher or cheaper, so it complements the dashboard decision rather than replacing it.
- How do you defend prerendering the landing page to someone who wants edits live instantly?I would separate the requirement from the mechanism. Instant edits do not require per-request rendering; they require a way to replace the stored page when the CMS publishes. On-demand invalidation gives them that while keeping static economics for the other 99.9% of requests, which is a better deal than rendering on every visit.
saying these in an interview costs you the question
- Picks one rendering mode for the entire application
- Says make everything dynamic to be safe, ignoring cost and resilience
- Wants to cache a per-user dashboard to improve its latency
- Justifies a mode purely by SEO without mentioning freshness
- Cannot state what staleness the page actually tolerates