Astro Framework Patterns
Quick Guide: Astro renders pages to static HTML by default with zero client-side JavaScript. Use
.astrocomponents for all static content, addclient:*directives only on interactive framework components (React/Vue/Svelte). Use content collections for type-safe structured content. Choose between static (default) and on-demand (SSR) rendering per-page withexport const prerender.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST keep pages static by default - only add export const prerender = false when the page genuinely needs request-time data)
(You MUST use client:* directives on framework components that need interactivity - without a directive, components render to static HTML with zero JavaScript)
(You MUST define content collections in src/content.config.ts with Zod schemas for type-safe frontmatter)
(You MUST use <ClientRouter /> from astro:transitions for View Transitions - the old <ViewTransitions /> component is removed in Astro 6)
(You MUST install a server adapter (@astrojs/node, @astrojs/vercel, etc.) before using on-demand rendering)
(You MUST use getStaticPaths() for dynamic routes in static mode - it is not needed for on-demand (SSR) routes)
</critical_requirements>
Auto-detection: Astro, .astro files, astro.config, islands architecture, client:load, client:visible, client:idle, client:only, client:media, server:defer, content collections, defineCollection, defineLiveCollection, getCollection, getLiveCollection, getEntry, getLiveEntry, render, astro:content, astro:transitions, ClientRouter, getStaticPaths, Astro.props, Astro.params, Astro.cookies, Astro.redirect, prerender, astro add, @astrojs/react, @astrojs/vue, @astrojs/svelte, Starlight
When to use:
- Building content-driven websites (blogs, docs, marketing, portfolios)
- Sites where most pages are static with selective interactivity (islands)
- Projects using content collections for structured Markdown/MDX/YAML content
- Multi-framework projects mixing React, Vue, Svelte, or Solid components
- Sites needing hybrid rendering (static pages + some server-rendered pages)
When NOT to use:
- Highly interactive web applications (dashboards, real-time collaboration) - use a full-stack SSR framework or SPA
- Apps where every page requires user authentication and dynamic data - use a full-stack SSR framework
- Projects that need React Server Components or Server Actions - use a React SSR framework
Key patterns covered:
- Astro component syntax (.astro files, frontmatter, template expressions, slots)
- Islands architecture (client directives, server islands, selective hydration)
- Content collections (schemas, querying, rendering, references, live collections)
- File-based routing (static routes, dynamic routes, rest parameters, pagination)
- Rendering modes (static, on-demand/SSR, hybrid with prerender control)
- View Transitions (ClientRouter, transition directives, persist state)
- Framework integrations (React, Vue, Svelte, Solid islands)
Detailed Resources:
- For decision frameworks and anti-patterns, see reference.md
Core patterns:
- examples/core.md - Astro components, props, slots, expressions, layouts
- examples/islands.md - Client directives, server islands, multi-framework islands
- examples/content.md - Content collections, schemas, querying, rendering
- examples/routing.md - File-based routing, dynamic routes, SSR/SSG modes
- examples/integrations.md - React/Vue/Svelte islands, View Transitions
<philosophy>
Philosophy
Astro is a content-first web framework that ships zero JavaScript by default. It pioneered the islands architecture where most of the page is fast static HTML, with small interactive "islands" of JavaScript hydrated only where needed.
Core principles:
- Content-first - Optimized for content-driven sites (blogs, docs, marketing, e-commerce)
- Zero JS by default - Components render to static HTML unless explicitly hydrated
- Islands architecture - Interactive components hydrate independently, reducing JavaScript payloads
- UI-agnostic - Use React, Vue, Svelte, Solid, Preact, or plain Astro components
- File-based routing -
src/pages/directory structure maps directly to URLs - Type-safe content - Content collections with Zod schemas enforce structure and provide TypeScript types
- Hybrid rendering - Mix static (SSG) and on-demand (SSR) pages in the same project
When to use Astro:
- Content-driven websites (blogs, documentation, portfolios, marketing)
- Sites with mostly static content and occasional interactivity
- Documentation sites (Starlight integration)
- E-commerce product pages with interactive carts
- Multi-framework projects where teams use different UI libraries
When NOT to use Astro:
- Fully interactive web applications (use a full-stack SSR framework or SPA)
- Real-time collaborative apps (use a dedicated SPA with WebSocket support)
- Projects requiring React Server Components or Server Actions (use a React SSR framework)
<patterns>
Core Patterns
Pattern 1: Astro Component Structure
Astro components (.astro files) have two parts: a frontmatter script block (between --- fences) and an HTML template.
Component Anatomy
---
// Component Script (frontmatter) - runs on the server only
import Layout from "../layouts/Layout.astro";
import { getCollection } from "astro:content";
// Props accessed via Astro.props
interface Props {
title: string;
description?: string;
}
const { title, description = "Default description" } = Astro.props;
// Server-side data fetching
const posts = await getCollection("blog");
---
<!-- Component Template - HTML with expressions -->
<Layout title={title}>
<h1>{title}</h1>
<p>{description}</p>
<ul>
{posts.map((post) => (
<li>
<a href={`/blog/${post.id}`}>{post.data.title}</a>
</li>
))}
</ul>
</Layout>
<style>
/* Scoped to this component by default */
h1 {
color: navy;
font-size: 2rem;
}
</style>
Why good: Frontmatter runs server-only (no JavaScript shipped), type-safe props with interface, scoped styles prevent leakage, expressions use JSX-like syntax
Pattern 2: Slots for Composition
Slots allow parent components to inject content into child component templates.
Default and Named Slots
---
// src/components/Card.astro
interface Props {
title: string;
}
const { title } = Astro.props;
---
<article class="card">
<header>
<slot name="header">
<h2>{title}</h2>
</slot>
</header>
<div class="body">
<slot /> <!-- Default slot -->
</div>
<footer>
<slot name="footer">
<p>Default footer</p>
</slot>
</footer>
</article>
---
// Usage in a page
import Card from "../components/Card.astro";
---
<Card title="My Card">
<span slot="header"><h2>Custom Header</h2></span>
<p>This goes in the default slot.</p>
<div slot="footer">
<a href="/more">Read more</a>
</div>
</Card>
Why good: Named slots provide flexible composition, fallback content renders when no slot content is provided, matches Web Component slot semantics
Pattern 3: Layouts
Layouts are Astro components that wrap page content with shared UI (header, footer, navigation).
Base Layout with Metadata
---
// src/layouts/BaseLayout.astro
interface Props {
title: string;
description?: string;
}
const { title, description = "My Astro Site" } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewp