# Astro Documentation Astro is an all-in-one web framework for building fast, content-focused websites. It uses a component-based architecture where `.astro` components render to HTML at build time or on-demand, with zero JavaScript sent to the browser by default. Astro's "Islands Architecture" allows you to add interactive UI components from any framework (React, Vue, Svelte, etc.) as isolated "islands" of interactivity while keeping the rest of the page static. The framework excels at content-driven sites like blogs, documentation, portfolios, and marketing pages. Key features include Content Collections for type-safe content management, built-in Markdown/MDX support, automatic image optimization, server-side rendering (SSR) with adapters for various hosting platforms, and View Transitions for smooth page navigation. Astro integrates seamlessly with headless CMSs, databases, and authentication providers. ## CLI Commands The Astro CLI provides commands for development, building, and previewing your site. The dev server uses Hot Module Replacement for instant updates. ```bash # Create a new Astro project npm create astro@latest # Start development server (default port 4321) npx astro dev # Build for production npx astro build # Preview production build locally npx astro preview # Type-check your project npx astro check # Generate TypeScript types for Astro modules npx astro sync # Add an integration (React, Tailwind, etc.) npx astro add react # Common flags npx astro dev --port 8080 --host npx astro build --verbose ``` ## Astro Components Astro components are `.astro` files with a frontmatter script (JavaScript/TypeScript) and HTML template. They render on the server with no client-side JavaScript by default. ```astro --- // src/components/Greeting.astro // Component Script - runs at build time or on request // Import other components import Button from './Button.astro'; // Accept props with TypeScript interface interface Props { name: string; greeting?: string; } const { name, greeting = "Hello" } = Astro.props; // Fetch data (runs server-side only) const response = await fetch('https://api.example.com/user'); const user = await response.json(); ---

{greeting}, {name}!

Welcome back, {user.displayName}

``` ## Slots Slots allow you to pass child content into components, similar to React's children prop. Named slots enable multiple content areas. ```astro --- // src/components/Card.astro interface Props { title: string; } const { title } = Astro.props; ---

{title}

``` ```astro --- // Usage in another component import Card from './Card.astro'; --- Custom Header Content

This goes in the default slot.

So does this paragraph.

``` ## Astro Render Context (Astro Global) The `Astro` global object provides runtime information and utilities in `.astro` files. In API endpoints, these are available on the `context` parameter. ```astro --- // src/pages/dashboard.astro // Access component props passed from parent const { title } = Astro.props; // Get URL information const currentUrl = Astro.url; // Full URL object const pathname = Astro.url.pathname; // e.g., "/dashboard" const searchParams = Astro.url.searchParams.get('filter'); // Access route parameters for dynamic routes [slug].astro const { slug } = Astro.params; // Get the site URL from config const siteUrl = Astro.site; // URL or undefined // Astro version for meta tags const generator = Astro.generator; // "Astro v5.x.x" // Access request object (headers, method, etc.) const userAgent = Astro.request.headers.get('user-agent'); const method = Astro.request.method; // Get client IP (SSR only) const clientIP = Astro.clientAddress; // Access data from middleware const userData = Astro.locals.user; // Set response headers/status Astro.response.status = 404; Astro.response.headers.set('Cache-Control', 'max-age=3600'); // Redirect to another page if (!userData) { return Astro.redirect('/login', 302); } // Rewrite to serve different content without redirect if (pathname === '/old-page') { return Astro.rewrite('/new-page'); } // i18n utilities const locale = Astro.currentLocale; const preferred = Astro.preferredLocale; ---

Dashboard for {userData.name}

Current path: {pathname}

``` ## API Endpoints Server endpoints handle API requests with full access to the request/response cycle. Export functions named after HTTP methods. ```typescript // src/pages/api/users/[id].ts import type { APIContext } from 'astro'; // Handle GET requests export async function GET({ params, request }: APIContext) { const { id } = params; try { const user = await db.users.findById(id); if (!user) { return new Response(JSON.stringify({ error: 'User not found' }), { status: 404, headers: { 'Content-Type': 'application/json' } }); } return new Response(JSON.stringify(user), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ error: 'Server error' }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } } // Handle POST requests export async function POST({ request, cookies, redirect }: APIContext) { const data = await request.json(); // Validate input if (!data.email || !data.name) { return new Response(JSON.stringify({ error: 'Missing fields' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } const newUser = await db.users.create(data); // Set a cookie cookies.set('user-id', newUser.id, { path: '/', httpOnly: true, secure: true, maxAge: 60 * 60 * 24 * 7 // 1 week }); return new Response(JSON.stringify(newUser), { status: 201, headers: { 'Content-Type': 'application/json' } }); } // Handle DELETE requests export async function DELETE({ params }: APIContext) { await db.users.delete(params.id); return new Response(null, { status: 204 }); } ``` ## Content Collections Content Collections provide type-safe content management with Zod schema validation. Define collections in `src/content.config.ts`. ```typescript // src/content.config.ts import { defineCollection, reference } from 'astro:content'; import { glob, file } from 'astro/loaders'; import { z } from 'astro/zod'; // Blog collection from markdown files const blog = defineCollection({ loader: glob({ pattern: '**/*.md', base: './src/content/blog' }), schema: z.object({ title: z.string(), description: z.string(), pubDate: z.coerce.date(), updatedDate: z.coerce.date().optional(), author: reference('authors'), // Reference another collection tags: z.array(z.string()), draft: z.boolean().default(false), image: z.object({ src: z.string(), alt: z.string(), }).optional(), }) }); // Authors collection from JSON const authors = defineCollection({ loader: file('src/data/authors.json'), schema: z.object({ id: z.string(), name: z.string(), email: z.string().email(), avatar: z.string().url(), bio: z.string().optional(), }) }); // Remote data with custom loader const products = defineCollection({ loader: async () => { const response = await fetch('https://api.example.com/products'); const data = await response.json(); return data.map((product: any) => ({ id: product.sku, ...product, })); }, schema: z.object({ name: z.string(), price: z.number(), category: z.enum(['electronics', 'clothing', 'books']), }) }); export const collections = { blog, authors, products }; ``` ## Querying Collections Use `getCollection()` and `getEntry()` to query content collections with full type safety. ```astro --- // src/pages/blog/index.astro import { getCollection, getEntry, render } from 'astro:content'; // Get all non-draft blog posts, sorted by date const posts = (await getCollection('blog', ({ data }) => { return data.draft !== true; })).sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); // Get a single entry by ID const featuredPost = await getEntry('blog', 'welcome-post'); ---

Blog

``` ```astro --- // src/pages/blog/[...slug].astro import { getCollection, getEntry, render } from 'astro:content'; // Generate static paths for all posts export async function getStaticPaths() { const posts = await getCollection('blog'); return posts.map((post) => ({ params: { slug: post.id }, props: { post }, })); } const { post } = Astro.props; // Render markdown content to HTML const { Content, headings } = await render(post); // Get referenced author data const author = await getEntry(post.data.author); ---

{post.data.title}

By {author.data.name}

``` ## Actions Actions are type-safe server functions callable from the client with automatic validation. Define actions in `src/actions/index.ts`. ```typescript // src/actions/index.ts import { defineAction, ActionError } from 'astro:actions'; import { z } from 'astro/zod'; export const server = { // Simple action with JSON input createComment: defineAction({ input: z.object({ postId: z.string(), content: z.string().min(1).max(500), authorEmail: z.string().email(), }), handler: async (input, context) => { // Check authentication from middleware locals if (!context.locals.user) { throw new ActionError({ code: 'UNAUTHORIZED', message: 'You must be logged in to comment', }); } const comment = await db.comments.create({ ...input, authorId: context.locals.user.id, createdAt: new Date(), }); return { id: comment.id, success: true }; }, }), // Form action with file upload uploadAvatar: defineAction({ accept: 'form', input: z.object({ file: z.instanceof(File), userId: z.string(), }), handler: async ({ file, userId }) => { const buffer = await file.arrayBuffer(); const url = await uploadToStorage(buffer, file.name); await db.users.update(userId, { avatarUrl: url }); return { url }; }, }), // Grouped actions newsletter: { subscribe: defineAction({ accept: 'form', input: z.object({ email: z.string().email(), topics: z.array(z.string()).optional(), }), handler: async (input) => { await mailchimp.subscribe(input.email, input.topics); return { subscribed: true }; }, }), unsubscribe: defineAction({ input: z.object({ email: z.string().email() }), handler: async ({ email }) => { await mailchimp.unsubscribe(email); return { unsubscribed: true }; }, }), }, }; ``` ```astro --- // src/pages/post/[id].astro - Using actions in Astro components import { actions } from 'astro:actions'; // Call action from server-side const result = await Astro.callAction(actions.newsletter.subscribe, { email: 'user@example.com' }); ---
``` ## Middleware Middleware intercepts requests and responses to inject behavior, modify data, or perform authentication before pages render. ```typescript // src/middleware.ts import { defineMiddleware, sequence } from 'astro:middleware'; // Authentication middleware const auth = defineMiddleware(async (context, next) => { const sessionToken = context.cookies.get('session')?.value; if (sessionToken) { try { const user = await verifySession(sessionToken); context.locals.user = user; } catch { context.cookies.delete('session'); } } return next(); }); // Logging middleware const logging = defineMiddleware(async (context, next) => { const start = Date.now(); const response = await next(); const duration = Date.now() - start; console.log(`${context.request.method} ${context.url.pathname} - ${duration}ms`); return response; }); // Protected routes middleware const protectRoutes = defineMiddleware(async (context, next) => { const protectedPaths = ['/dashboard', '/settings', '/admin']; const isProtected = protectedPaths.some(p => context.url.pathname.startsWith(p)); if (isProtected && !context.locals.user) { // Rewrite to login page without changing URL return context.rewrite('/login'); } return next(); }); // Response modification middleware const addHeaders = defineMiddleware(async (context, next) => { const response = await next(); // Add security headers response.headers.set('X-Frame-Options', 'DENY'); response.headers.set('X-Content-Type-Options', 'nosniff'); return response; }); // Chain multiple middlewares export const onRequest = sequence(logging, auth, protectRoutes, addHeaders); ``` ```typescript // src/env.d.ts - Type the locals object declare namespace App { interface Locals { user?: { id: string; email: string; role: 'admin' | 'user'; }; } } ``` ## Configuration Reference Configure Astro in `astro.config.mjs` with options for build, server, integrations, and more. ```javascript // astro.config.mjs import { defineConfig } from 'astro/config'; import react from '@astrojs/react'; import tailwind from '@astrojs/tailwind'; import mdx from '@astrojs/mdx'; import node from '@astrojs/node'; export default defineConfig({ // Site URL for sitemap and canonical URLs site: 'https://example.com', // Base path for deployment to subdirectory base: '/docs', // Output mode: 'static' (default) or 'server' for SSR output: 'server', // Server adapter for SSR deployment adapter: node({ mode: 'standalone' }), // Integrations for frameworks, tools, and features integrations: [ react(), tailwind(), mdx(), ], // Trailing slash behavior: 'always', 'never', or 'ignore' trailingSlash: 'always', // Redirects configuration redirects: { '/old-page': '/new-page', '/blog/[...slug]': '/articles/[...slug]', }, // Development server options server: { port: 4321, host: true, // Expose to network }, // Build options build: { format: 'directory', // or 'file' assets: '_assets', inlineStylesheets: 'auto', }, // Image optimization image: { service: { entrypoint: 'astro/assets/services/sharp' }, domains: ['images.unsplash.com'], remotePatterns: [{ protocol: 'https', hostname: '**.cloudinary.com', }], }, // Markdown processing markdown: { syntaxHighlight: 'shiki', shikiConfig: { theme: 'github-dark', wrap: true, }, remarkPlugins: [], rehypePlugins: [], }, // i18n configuration i18n: { defaultLocale: 'en', locales: ['en', 'es', 'fr'], routing: { prefixDefaultLocale: false, }, fallback: { fr: 'en', }, }, // Environment variables schema env: { schema: { API_URL: envField.string({ context: 'server', access: 'secret' }), PUBLIC_SITE_NAME: envField.string({ context: 'client', access: 'public' }), }, }, // Prefetch configuration prefetch: { prefetchAll: true, defaultStrategy: 'hover', }, // Vite configuration passthrough vite: { plugins: [], ssr: { external: ['some-package'], }, }, }); ``` ## Cookie Management Cookies are available in SSR contexts for reading and writing session data, authentication tokens, and user preferences. ```astro --- // src/pages/api/auth.ts or .astro pages export const prerender = false; // Required for cookie access // Read cookies const sessionId = Astro.cookies.get('session')?.value; const preferences = Astro.cookies.get('prefs')?.json(); const visitCount = Astro.cookies.get('visits')?.number() ?? 0; // Set cookies with options Astro.cookies.set('session', 'abc123', { path: '/', httpOnly: true, secure: true, sameSite: 'lax', maxAge: 60 * 60 * 24 * 7, // 1 week in seconds }); // Set JSON cookie Astro.cookies.set('preferences', { theme: 'dark', language: 'en' }, { path: '/', }); // Delete cookie Astro.cookies.delete('session'); // Check if cookie exists const hasSession = Astro.cookies.has('session'); --- ``` ## Sessions Sessions provide persistent server-side storage for user data across requests, configured with various storage drivers. ```javascript // astro.config.mjs export default defineConfig({ output: 'server', session: { driver: 'redis', options: { url: process.env.REDIS_URL, }, ttl: 3600, // Session expires after 1 hour cookie: { name: 'app-session', sameSite: 'lax', httpOnly: true, secure: true, }, }, }); ``` ```astro --- // src/pages/cart.astro export const prerender = false; // Get session data const cart = await Astro.session?.get('cart') ?? []; const lastViewed = await Astro.session?.get('lastViewedProduct'); // Set session data (with optional per-key TTL) Astro.session?.set('cart', [...cart, newItem]); Astro.session?.set('flash', 'Item added!', { ttl: 60 }); // 60 seconds // On login - regenerate session ID to prevent fixation Astro.session?.regenerate(); // On logout - destroy session entirely Astro.session?.destroy(); ---

Your Cart ({cart.length} items)

``` ## Dynamic Routes Dynamic routes use bracket syntax in filenames to match variable URL segments and generate multiple pages from data. ```astro --- // src/pages/blog/[slug].astro - Single dynamic segment import { getCollection } from 'astro:content'; export async function getStaticPaths() { const posts = await getCollection('blog'); return posts.map((post) => ({ params: { slug: post.id }, props: { post }, })); } const { post } = Astro.props; ---

{post.data.title}

``` ```astro --- // src/pages/[...path].astro - Catch-all route (rest parameter) export async function getStaticPaths() { const pages = await fetchPagesFromCMS(); return pages.map((page) => ({ params: { path: page.slug || undefined }, // undefined matches root props: { page }, })); } const { page } = Astro.props; --- ``` ```astro --- // src/pages/products/[category]/[id].astro - Multiple segments export async function getStaticPaths() { const products = await getProducts(); return products.map((p) => ({ params: { category: p.category, id: p.id }, props: { product: p }, })); } --- ``` ## UI Framework Integration Astro supports multiple UI frameworks (React, Vue, Svelte, etc.) as interactive "islands" with client directives controlling when components hydrate. ```astro --- // src/pages/index.astro import ReactCounter from '../components/ReactCounter.jsx'; import VueCard from '../components/VueCard.vue'; import SvelteForm from '../components/SvelteForm.svelte'; --- ``` ```jsx // src/components/ReactCounter.jsx import { useState } from 'react'; export default function Counter({ initialCount = 0 }) { const [count, setCount] = useState(initialCount); return (

Count: {count}

); } ``` ## Summary Astro is ideal for building content-rich websites where performance matters. Its zero-JS-by-default approach delivers fast page loads while still supporting interactive components through the Islands Architecture. Content Collections provide a robust, type-safe way to manage Markdown, MDX, JSON, or remote content with automatic validation and TypeScript integration. Actions simplify server-client communication with type-safe RPC calls. Common integration patterns include using Astro for documentation sites with MDX content, blogs with markdown and frontmatter, e-commerce storefronts with SSR for dynamic pricing, and marketing sites with forms handled by Actions. The framework works seamlessly with headless CMSs (Contentful, Sanity, Strapi), databases (Prisma, Drizzle), authentication providers (Auth.js, Lucia), and deployment platforms (Vercel, Netlify, Cloudflare, Node.js servers) through its adapter system.