Rendering Modes: Static, Server & Hybrid
Every earlier chapter used Astro's default: output: 'static'. This chapter covers the other side — output: 'server', and mixing both per page — which removes several constraints already flagged along the way, including Chapter 1's own promise that Astro "isn't only for static blogs."
Static Output — the Default, Recapped
Every page pre-rendered to plain HTML files at build time. getStaticPaths() (Chapter 3) is required for any dynamic route, since every possible path has to be known up front — there's no server running afterward to resolve one on demand.
Server Output: Per-Request Rendering
output: 'server' renders every page fresh, per request. Astro itself doesn't ship a production server runtime — an adapter (@astrojs/node, @astrojs/vercel, @astrojs/netlify, and others) is what actually runs the server on a given platform, installed via npx astro add node.
[slug].astro reads Astro.params.slug directly and resolves per request — no getStaticPaths() function needed at all. This is exactly the change Chapter 3's own finding-box promised: the "every path must be known at build time" constraint was specific to static output, not to Astro itself.
Mixing Both: Per-Page Opt-Out
A single page can opt out of the project's own default mode via export const prerender — a mostly-static project can mark one genuinely dynamic page (a live dashboard, an admin panel) for server rendering, while everything else stays pre-built and fast. The reverse works too: in a server-output project, a page that's genuinely static (a privacy policy, an about page) can be marked prerender = true to skip per-request rendering for content that never changes.
output: 'server' (or the per-page opt-out shown above) plus a real adapter, for exactly the same reason: an admin interface reading and writing live data has no meaningful static-build equivalent.
Three Rendering Approaches, Compared
| Static (default) | Server | Per-page opt-out | |
|---|---|---|---|
| When it renders | Once, at build time | Every request | Mixed, page by page |
| Needs an adapter? | No | Yes | Yes, for the dynamic pages |
getStaticPaths() needed? | Yes, for dynamic routes | No | Only for the pages still using static rendering |
| Good fit for | Blogs, docs, marketing pages | An admin panel, live data | A content site with one genuinely dynamic section |
Coding Challenges
Switch a project to output: 'server' with the Node adapter, remove getStaticPaths() from a dynamic route built in Chapter 3, and confirm it still resolves correctly per-request.
📄 View solutionIn that same server-mode project, add export const prerender = true to one specific static page, and confirm it's still pre-rendered at build time while the rest of the site renders per-request.
📄 View solutionBuild a POST API endpoint that only works correctly in server output mode, and explain concretely why a purely static build couldn't support it.
📄 View solutionChapter 10 Quick Reference
output: 'static'— the default; pre-rendered once, requiresgetStaticPaths()for dynamic routesoutput: 'server'— per-request rendering; requires an adapter (@astrojs/node,@astrojs/vercel, etc.)export const prerender— opts a single page into or out of the project's own default mode- Server mode removes
getStaticPaths()entirely — dynamic routes resolve fresh, per request - Rebuild groundwork — a future rebuild course's own admin interface needs server output, the same conclusion every sibling rebuild course already reached
- Next chapter: Markdown & MDX Content Authoring