---
name: astro
description: Builds content-focused websites with Astro using islands architecture, content collections, and multi-framework support. Use when creating static sites, blogs, documentation, marketing pages, or content-heavy applications with minimal JavaScript.
---
# Astro
Content-focused web framework with islands architecture for building fast static and server-rendered websites with minimal JavaScript.
## Quick Start
**Create new project:**
```bash
npm create astro@latest my-site
cd my-site
npm run dev
```
**Essential file structure:**
```
src/
pages/ # File-based routing
index.astro # Home page (/)
about.astro # /about
blog/
[slug].astro # /blog/:slug
components/ # Reusable components
layouts/ # Page layouts
content/ # Content collections
blog/ # Blog posts collection
styles/ # Global styles
public/ # Static assets
astro.config.mjs # Astro configuration
```
## Astro Components
### Basic Syntax
```astro
---
// Component Script (runs at build time)
import Header from '../components/Header.astro';
import Button from '../components/Button.tsx';
interface Props {
title: string;
description?: string;
}
const { title, description = 'Default description' } = Astro.props;
// Fetch data at build time
const response = await fetch('https://api.example.com/data');
const data = await response.json();
---
{title}
{title}
{data.items.map((item) => (
- {item.name}
))}
```
### Props and Types
```astro
---
interface Props {
title: string;
tags: string[];
publishDate: Date;
featured?: boolean;
}
const { title, tags, publishDate, featured = false } = Astro.props;
---
{title}
{tags.map((tag) => - {tag}
)}
```
### Slots
```astro
---
// Card.astro
interface Props {
title: string;
}
const { title } = Astro.props;
---
```
**Using slots:**
```astro
Custom Header
Main content goes here
```
## Islands Architecture
### Client Directives
Components are static by default. Add `client:*` directives for interactivity:
| Directive | When JavaScript Loads |
|-----------|----------------------|
| `client:load` | Immediately on page load |
| `client:idle` | When browser becomes idle |
| `client:visible` | When component enters viewport |
| `client:media` | When media query matches |
| `client:only` | Skip SSR, client render only |
```astro
---
import Counter from '../components/Counter.tsx';
import Newsletter from '../components/Newsletter.vue';
import Comments from '../components/Comments.svelte';
---
```
### Framework Integrations
```bash
# Add React
npx astro add react
# Add Vue
npx astro add vue
# Add Svelte
npx astro add svelte
# Add SolidJS
npx astro add solid
```
**Using multiple frameworks:**
```astro
---
import ReactComponent from '../components/ReactComponent.tsx';
import VueComponent from '../components/VueComponent.vue';
import SvelteComponent from '../components/SvelteComponent.svelte';
---
```
## File-Based Routing
### Static Routes
```
src/pages/
index.astro # /
about.astro # /about
contact.astro # /contact
blog/
index.astro # /blog
first-post.astro # /blog/first-post
```
### Dynamic Routes
```astro
---
// src/pages/blog/[slug].astro
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
{post.data.title}
```
### Rest Parameters
```astro
---
// src/pages/docs/[...slug].astro
// Matches /docs, /docs/intro, /docs/guides/getting-started
export function getStaticPaths() {
return [
{ params: { slug: undefined } }, // /docs
{ params: { slug: 'intro' } }, // /docs/intro
{ params: { slug: 'guides/start' } }, // /docs/guides/start
];
}
const { slug } = Astro.params;
---
```
## Content Collections
### Define Collections
```typescript
// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
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(),
heroImage: z.string().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});
const authors = defineCollection({
loader: glob({ pattern: '**/*.json', base: './src/content/authors' }),
schema: z.object({
name: z.string(),
bio: z.string(),
avatar: z.string(),
social: z.object({
twitter: z.string().optional(),
github: z.string().optional(),
}),
}),
});
export const collections = { blog, authors };
```
### Query Collections
```astro
---
import { getCollection, getEntry } from 'astro:content';
// Get all published posts
const allPosts = await getCollection('blog', ({ data }) => {
return data.draft !== true;
});
// Sort by date
const sortedPosts = allPosts.sort(
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
// Get single entry
const featuredPost = await getEntry('blog', 'featured-post');
---
```
### Render Content
```astro
---
import { getEntry } from 'astro:content';
const post = await getEntry('blog', 'my-post');
const { Content, headings } = await post.render();
---
{post.data.title}
```
## Layouts
### Basic Layout
```astro
---
// src/layouts/BaseLayout.astro
interface Props {
title: string;
description?: string;
}
const { title, description = 'My Astro site' } = Astro.props;
---
{title}
```
**Using layouts:**
```astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
Welcome!
This is the home page.
```
### Markdown Layout
```astro
---
// src/layouts/BlogPost.astro
import BaseLayout from './BaseLayout.astro';
import { type CollectionEntry } from 'astro:content';
interface Props {
post: CollectionEntry<'blog'>;
}
const { post } = Astro.props;
const { title, pubDate, heroImage } = post.data;
---
{heroImage &&
}
{title}
```
## Server-Side Rendering
### Enable SSR
```javascript
// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'server', // or 'hybrid'
adapter: node({
mode: 'standalone',
}),
});
```
### Server Endpoints
```typescript
// src/pages/api/posts.json.ts
import type { APIRoute } from 'astro';
export const GET: APIRoute = async ({ request }) => {
const posts = await getPosts();
return new Response(JSON.stringify(posts), {
headers: { 'Content-Type': 'application/json' },
});
};
export const POST: APIRoute = async ({ request }) => {
const data = await request.json();
const post = await createPost(data);
return new Response(JSON.stringify(post), {
status: 201,
headers: { 'Content-Type': 'application/json' },
});
};
```
### Hybrid Rendering
```javascript
// astro.config.mjs
export default defineConfig({
output: 'hybrid', // Static by default, opt-in to SSR
});
```
```astro
---
// This page renders on each request
export const prerender = false;
const user = await getUser(Astro.cookies.get('session'));
---
```
## Styling
### Scoped Styles
```astro
```
### Global Styles
```astro
```
### CSS Variables
```astro
---
const { color = 'blue' } = Astro.props;
---
Content
```
### Tailwind CSS
```bash
npx astro add tailwind
```
```astro
Hello
```
## View Transitions
```astro
---
import { ViewTransitions } from 'astro:transitions';
---
```
**Custom transitions:**
```astro
```
## Image Optimization
```astro
---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.png';
---
```
## Environment Variables
```bash
# .env
PUBLIC_API_URL=https://api.example.com
SECRET_KEY=abc123
```
```astro
---
// Server-side (secret)
const secret = import.meta.env.SECRET_KEY;
// Client-side (public)
const apiUrl = import.meta.env.PUBLIC_API_URL;
---
```
## Best Practices
1. **Default to static** - Only add interactivity where needed
2. **Use content collections** - For any structured content
3. **Lazy load islands** - Use `client:visible` for below-fold content
4. **Colocate styles** - Use scoped styles in components
5. **Optimize images** - Use `astro:assets` for automatic optimization
## Common Mistakes
| Mistake | Fix |
|---------|-----|
| Adding `client:*` everywhere | Only for truly interactive components |
| Large client bundles | Split into smaller islands |
| Not using content collections | For blogs, docs, use collections |
| Fetching in client components | Fetch in Astro component script |
| Ignoring `getStaticPaths` | Required for dynamic routes |
## Reference Files
- [references/content-collections.md](references/content-collections.md) - Advanced collection patterns
- [references/islands.md](references/islands.md) - Islands architecture deep dive
- [references/deployment.md](references/deployment.md) - Deployment options
## Templates
- [templates/page.astro](templates/page.astro) - Page component template
- [templates/layout.astro](templates/layout.astro) - Layout component template