Astro
Astro is the web framework for content-driven websites: server-first, static HTML by default, with opt-in interactive islands.
Always consult the official docs for the latest API details. Fetch https://docs.astro.build/llms-small.txt for an abridged reference, or https://docs.astro.build/llms-full.txt for the complete documentation.
You already know Astro fundamentals — .astro component syntax, client:* directives, file-based routing, middleware, scoped styles. This skill covers what changed in v5/v6 and the newer APIs you may know imperfectly. When unsure, fetch the docs above instead of guessing.
Astro v6 Key Changes
- Node.js 22.12.0+ required (Node 18 and 20 dropped; odd-numbered versions not supported)
- Vite 7 and Zod 4 — Zod string validators moved to top-level:
z.email()instead ofz.string().email(); error messages useerror:instead ofmessage: import.meta.envvalues always inlined, never coerced — useprocess.envfor server-only secrets- Images: cropping by default when both
widthandheightset; never upscales - Endpoints with file extensions no longer accessible with trailing slash
- Shiki 4 for syntax highlighting
- Upgrade with
npx @astrojs/upgrade
Rendering Model
All pages are pre-rendered (static) by default. On-demand rendering requires an adapter (npx astro add node|vercel|netlify|cloudflare) plus per-page opt-out:
---
export const prerender = false;
---
Or set output: 'server' in config to flip the default, then opt static pages back in with export const prerender = true.
Server Islands
Defer server rendering of slow or personalized components without blocking the static page:
<Avatar server:defer>
<GenericAvatar slot="fallback" /> <!-- shown while island loads -->
</Avatar>
Requires an adapter. Server islands can access cookies and sessions; props must be serializable. Use the Referer header to access the parent page URL from within a server island.
Content Collections (Content Layer)
Collections are defined in src/content.config.ts with loaders (v5+ API — replaces the legacy type: 'content' folders):
import { defineCollection } from 'astro:content';
import { glob, file } from 'astro/loaders';
import { z } from 'astro/zod';
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
- Built-in loaders:
glob()(directory of md/mdx/markdoc/json/yaml/toml),file()(single data file). Custom loaders can pull from any source. - Query with
getCollection()/getEntry()fromastro:content. - Live collections fetch at request time instead of build time — define with a live loader, query with
getLiveCollection()/getLiveEntry().
Actions
Type-safe server functions defined in src/actions/index.ts via defineAction({ input, handler }), called from the client as actions.myAction(data).
- Forms: set
accept: 'form'and usemethod="POST"withaction={actions.myAction}for zero-JS submissions; handle server-side results withAstro.getActionResult(). - Security: actions are public endpoints (
/_actions/<name>) — always validate authorization in the handler.
Sessions (v5.7+)
Server-side data storage across requests. Requires an adapter; Node, Cloudflare, and Netlify adapters configure a default storage driver automatically.
---
export const prerender = false;
const cart = await Astro.session?.get('cart');
await Astro.session?.set('cart', [...(cart || []), newItem]);
---
Available as Astro.session in components and context.session in endpoints/middleware/actions.
View Transitions
The router component is <ClientRouter /> from astro:transitions (renamed from <ViewTransitions /> in v5):
transition:name/transition:animate/transition:persistdirectives as before- Programmatic navigation:
import { navigate } from 'astro:transitions/client' data-astro-reloadon a link forces full-page navigation- Lifecycle events:
astro:before-preparation,astro:after-preparation,astro:before-swap,astro:after-swap,astro:page-load