Skip to content

Astro is a content-focused web framework designed for speed, utilizing an Islands architecture. It renders HTML on the server by default with zero client-side JavaScript overhead. Interactive UI framework components (React, Vue, Svelte) are isolated into standalone “islands” and hydrated individually on demand.

FeatureServer ExecutionClient Impact
Zero JS baselineRenders static HTMLFast initial load
Island hydrationIsolated JS bundlesLow memory footprint
Multi-frameworkRenders React/Vue/etcMix framework UI

Astro compiles .astro components into static HTML at build or request time. Frontmatter between --- fences runs exclusively on the server to fetch data, access environment variables, or process markup. Any HTML rendered in the template ships to the browser without framework runtime overhead.

---
import Card from '../components/Card.astro';
const items = ['Fast', 'Light', 'Flexible'];
---
<ul>
{items.map((item) => (
<li><Card title={item} /></li>
))}
</ul>

Gotcha: Frontmatter script (---) executes on the server only. Browser APIs like window, document, or localStorage are undefined in frontmatter and throw runtime errors if accessed there.

DirectiveHydration TimingTypical Use Case
client:loadImmediately on loadCritical header UI
client:idleBrowser idle periodLow-priority widgets
client:visibleViewport entryBelow-fold components
client:only="react"Client-side onlyNon-SSR browser UI

React components render as static HTML on the server unless given an explicit client:* hydration directive in an .astro template. When hydrated, Astro loads only the React runtime and component code required for that specific island, keeping the rest of the page unburdened by client JavaScript.

---
import Counter from '../components/Counter.jsx';
import Chart from '../components/Chart.jsx';
---
<!-- Hydrates when browser becomes idle -->
<Counter client:idle initial={5} />
<!-- Hydrates only when scrolled into view -->
<Chart client:visible data={[1, 2, 3]} />

Gotcha: Without a client:* directive, <Counter /> renders static HTML and ignores user clicks, event handlers, and React state updates.

Island state and cross-component communication

Section titled “Island state and cross-component communication”
StrategyScopeAdvantage
Shared storeClient module scopeFramework-agnostic sync
Custom EventsDOM window / nodeDecoupled event signaling
URL StateBrowser query paramsDeep-linkable UI state

Because each hydrated component is an isolated React root, standard React Context cannot pass state between separate islands on the same page. Module-level reactive stores or native DOM CustomEvent dispatchers sync state across React, Vue, Svelte, and native JS components without re-rendering the surrounding page.

type Listener = (count: number) => void;
let count = 0;
const listeners = new Set<Listener>();
export const store = {
get: () => count,
inc: () => {
count++;
listeners.forEach((fn) => fn(count));
},
sub: (fn: Listener) => {
listeners.add(fn);
return () => listeners.delete(fn);
},
};

Gotcha: React Context providers wrapped around one island do not reach another island. Use module-scope state or global browser events to communicate across island boundaries.

FeatureConfig FunctionPurpose
Collection configdefineCollection()Defines collection rules
Schema validationz.object()Validates YAML/JSON data
Data querygetCollection()Fetches typed entries

Astro Content Collections organize Markdown, MDX, JSON, or YAML content with compile-time type safety. Schemas defined using Zod validate frontmatter fields during build, preventing broken routes or missing metadata before deployment.

src/content.config.ts
---
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
pubDate: z.date(),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
---

Gotcha: getCollection('blog') returns unrendered content objects. Call await entry.render() on a single entry to obtain its rendered <Content /> component.

Method ExportHTTP TriggerExpected Return
export const GETGET requestResponse object
export const POSTPOST requestResponse object
export const ALLAny HTTP methodResponse object

Server endpoints defined in src/pages/ as .ts files output raw data instead of HTML. They handle HTTP requests, process webhooks, or return JSON payloads for client components and external service calls.

type APIRoute = (
ctx: { request: Request },
) => Response;
export const GET: APIRoute = ({ request }) => {
const url = new URL(request.url);
const q = url.searchParams.get('q') ?? '';
return new Response(JSON.stringify({ q }), {
headers: {
'Content-Type': 'application/json',
},
});
};

Gotcha: In static output mode (output: 'static'), API endpoints execute at build time unless individual routes specify export const prerender = false with a server adapter configured.

AI PatternImplementationPurpose
/llms.txtStatic text routeMachine-readable catalog
Clean Markdown APIRaw endpoint exportScraping-friendly context
Structured JSONServer API endpointAgent tool invocation

AI agents (LLMs, crawlers, coding assistants) consume web content best when it is structured, lightweight, and free of browser execution requirements. Serving plain text /llms.txt routes and Markdown content endpoints allows AI agents to inspect page indexes, query documentation, and consume knowledge bases directly.

type APIRoute = () => Response;
export const GET: APIRoute = () => {
const body = `# Site Map for AI Agents
- /docs/intro: Getting started guide
- /api/v1/schema: OpenAPI specs`;
return new Response(body, {
headers: { 'Content-Type': 'text/plain' },
});
};

Gotcha: AI crawlers cannot execute JavaScript or client hydration scripts. Ensure agent routes return static Markdown or JSON rather than relying on hydrated React islands to populate content.