Rendering Modes: Static, Server & Hybrid

Chapter 10
Rendering Modes: Static, Server & Hybrid
output: 'static' vs 'server', adapters, and per-page opt-out with prerender

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

// astro.config.mjs import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), });

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.

Chapter 3's getStaticPaths() constraint is gone
In server mode, a dynamic route like [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

// src/pages/dashboard.astro — even in a mostly static project --- export const prerender = false; // opt this one page into server rendering ---

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.

Groundwork for a future rebuild course's own deployment chapter
Every framework in the Website Rebuild series needed a real, dynamic admin CRUD interface — none of them could have built one with a purely static site generator. A future Website Rebuild with Astro course would need 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)ServerPer-page opt-out
When it rendersOnce, at build timeEvery requestMixed, page by page
Needs an adapter?NoYesYes, for the dynamic pages
getStaticPaths() needed?Yes, for dynamic routesNoOnly for the pages still using static rendering
Good fit forBlogs, docs, marketing pagesAn admin panel, live dataA content site with one genuinely dynamic section

Coding Challenges

Challenge 1

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 solution
Challenge 2

In 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 solution
Challenge 3

Build 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 solution

Chapter 10 Quick Reference

  • output: 'static' — the default; pre-rendered once, requires getStaticPaths() for dynamic routes
  • output: '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