Rendering

Astro gives you fine-grained control over how each route renders: static at build time, or on-demand per request. You set this per-route, not globally.

Output modes

output: 'static' (default)

Every page is pre-rendered to HTML at build time. This is the default and the fastest option for content sites.

// astro.config.mjs
export default defineConfig({
  output: 'static',  // default, can omit
});

To opt specific routes into on-demand rendering:

---
// src/pages/dashboard.astro
export const prerender = false;  // this route renders on each request
---

This requires an adapter (Node, Cloudflare, etc.) for the on-demand routes.

output: 'server'

All routes render on-demand by default. Opt specific routes into static prerendering:

---
// src/pages/about.astro
export const prerender = true;  // prerender this route at build time
---

No more output: 'hybrid'

Astro 5 removed hybrid mode. The current model is simpler:

  • static (default) + prerender = false on specific routes
  • server + prerender = true on specific routes

Both give you the same flexibility. The difference is just which is the default.

Adapters

On-demand rendering requires an adapter that tells Astro how to run on your host:

npx astro add node        # Node.js server
npx astro add cloudflare  # Cloudflare Workers/Pages
npx astro add vercel      # Vercel Functions
npx astro add netlify     # Netlify Functions
// astro.config.mjs
import node from '@astrojs/node';

export default defineConfig({
  output: 'static',
  adapter: node({ mode: 'standalone' }),
});

Even with output: 'static', you need an adapter if any route uses prerender = false.

When to use each mode

ScenarioRenderingWhy
Blog post, docs pageStatic (prerender)Content rarely changes, fastest possible
News story pageOn-demandContent changes frequently, needs fresh data
RSS feed endpointStatic or on-demandDepends on update frequency
Search results pageOn-demandDepends on query params
About pageStaticRarely changes
User dashboardOn-demandPersonalized content

Cache-Control headers

For on-demand routes, you control caching explicitly with HTTP headers:

---
export const prerender = false;

const data = await fetch('https://api.example.com/story/123').then(r => r.json());

// Set cache headers
Astro.response.headers.set('Cache-Control', 'public, max-age=300, s-maxage=600');
---
<h1>{data.title}</h1>

This is more transparent than Next.js’s cache behavior. You set HTTP headers directly rather than relying on framework-level caching abstractions.

Experimental route caching (Astro 6)

Astro 6 introduced experimental route caching with tag-based and path-based invalidation:

// astro.config.mjs
export default defineConfig({
  experimental: {
    routeCache: true,
  },
});

This is still experimental as of Astro 6.1. For production use, explicit Cache-Control headers with CDN caching is the safer choice.

Comparison with Next.js rendering

ConceptNext.jsAstro
DefaultServer Components (on-demand)Static (prerendered)
Static opt-inPages are static by default in App Router with no dynamic APIsexport const prerender = true
Dynamic opt-inDynamic APIs (cookies(), headers(), searchParams)export const prerender = false
Revalidationrevalidate, revalidateTag(), revalidatePath()HTTP Cache-Control headers (or experimental route caching)
ISRBuilt into Next.jsUse CDN cache + stale-while-revalidate headers
StreamingReact SuspenseNot applicable (no React tree to stream)