Static vs. Server Rendering
Choosing between Astro's static output and on-demand server rendering.
3 min de lectura
Every Astro project has an output mode, set in astro.config.mjs, that decides when your pages' frontmatter code runs: once, ahead of time, or fresh on every request. This choice affects hosting, data freshness, and what a page is even allowed to do.
Static output: the default
// astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
output: "static", // the default — this line is often omitted entirely
});In static mode, npm run build runs every page's frontmatter once, produces a folder of plain .html files (plus CSS/JS assets), and that's the entire deployable artifact. There's no server process needed at runtime — any static file host (Netlify, Vercel's static hosting, GitHub Pages, a plain CDN) can serve the result. This is the fastest possible way to serve a page, since there's no per-request computation at all — the visitor's request is answered with a file that was already sitting on disk.
The tradeoff: the data in those pages is only as fresh as the last build. A blog built from a content collection updates correctly every time you rebuild and redeploy, but nothing changes between deploys — there's no mechanism for a static page to reflect, say, a change made in a CMS five minutes ago, until the next build runs.
Server output: rendering per request
// astro.config.mjs
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
export default defineConfig({
output: "server",
adapter: node({ mode: "standalone" }),
});Server mode requires an adapter — a package targeting the platform that will actually run your server (Node, Vercel, Netlify Functions, Cloudflare Workers, etc.) — because now something has to execute your frontmatter code on every incoming request, rather than once at build time.
---
// This runs fresh on every request in server mode.
const response = await fetch("https://api.example.com/live-scores");
const scores = await response.json();
---
<ul>
{scores.map((s) => <li>{s.team}: {s.points}</li>)}
</ul>The same frontmatter syntax from earlier lessons behaves identically — await fetch(...) still just runs in the frontmatter — but now it happens per visitor, per request, so data that changes constantly (live scores, a logged-in user's dashboard, search results for a query string) can be reflected immediately rather than waiting for a rebuild.
Mixing modes with export const prerender
You don't have to choose one mode for an entire site. With output: "server" set globally, an individual page can opt back into build-time static rendering:
---
// src/pages/about.astro
export const prerender = true;
const aboutText = "This page never changes, so it's built once.";
---
<p>{aboutText}</p>This is the common real-world pattern: set the project to server mode so pages that need per-request data can have it, then mark the genuinely static pages — an About page, a Terms of Service page — with prerender = true so they're still built once and served as fast, cacheable static files rather than being needlessly recomputed on every visit.
Choosing between them
Default to static output for content that changes at the pace of a deploy — most blogs, docs, and marketing sites. Reach for server output specifically for the pages that need it: authenticated dashboards, search results, anything personalized per visitor or too frequently changing to pre-build. Astro's prerender flag means that choice doesn't have to be all-or-nothing across an entire site.