Astro is a web framework for building fast, content-driven websites. It ships zero JavaScript to the browser by default — a radical departure from the React-first era. Pages are rendered to static HTML at build time (or on the server), and interactive components hydrate independently as "islands" in an otherwise static sea. The result: blazing-fast pages with a developer experience that works with every major UI framework.
This guide covers everything you need to build with Astro — from your first project to advanced routing, content collections, server-side rendering, and beyond.
Note
Astro requires Node.js >= 22.12.0. Check your version with
node --versionbefore starting. Older Node versions will cause subtle build failures.
1. Quick Start
Get a project running in under two minutes.
npm create astro@latest
The interactive wizard asks for:
- Project name — directory to create
- Template — starter (blog, portfolio, minimal, etc.)
- Install dependencies — yes/no
- Initialize git — yes/no
Once scaffolded:
cd my-project
npm run dev
Open http://localhost:4321 in your browser. Your Astro site is live.
Tip
The default dev server port is 4321. If that port is in use, pass
--port 3000(or any free port) toastro dev. You can also setserver.portinastro.config.mjsto make it permanent.
What you get out of the box
| Feature | Default |
|---|---|
| Dev server | Vite-powered HMR on port 4321 |
| Build output | Static HTML in ./dist |
| Styling | Scoped CSS per component |
| Markdown | Built-in, with Shiki syntax highlighting |
| TypeScript | Supported natively, no config needed |
| Images | Automatic optimization via Sharp |
| Dev Toolbar | Enabled (audit tools, component inspector) |
Project structure (default)
my-project/
├── public/ Static assets (copied verbatim to dist/)
├── src/
│ ├── components/ Reusable .astro components
│ ├── layouts/ Page layout wrappers
│ ├── pages/ File-based routing (every file = a route)
│ └── content/ Content collections (blog posts, docs, etc.)
├── astro.config.mjs Project configuration
├── package.json
└── tsconfig.json
Every file in src/pages/ becomes a URL. src/pages/index.astro → /. src/pages/about.astro → /about. No router config needed.
2. Installation
Fresh project (recommended)
# npm
npm create astro@latest
# pnpm
pnpm create astro@latest
# yarn
yarn create astro
The create astro wizard handles everything. Pick a template, install deps, and you're ready.
Manual installation
If you prefer to wire things up yourself:
mkdir my-project && cd my-project
npm init -y
npm install astro
Add scripts to package.json:
{
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview"
}
}
Create src/pages/index.astro:
---
// Frontmatter (server-side JS/TS)
const title = "Hello, Astro!";
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>Welcome to my Astro site.</p>
</body>
</html>
Run npm run dev and visit http://localhost:4321.
Adding a UI framework
Astro works with React, Vue, Svelte, Preact, Solid.js, and Alpine.js. Use astro add to install and configure any of them automatically:
# React
npx astro add react
# Vue
npx astro add vue
# Svelte
npx astro add svelte
# Preact
npx astro add preact
# Solid.js
npx astro add solid-js
# Alpine.js
npx astro add alpinejs
astro add installs the package, updates astro.config.mjs, and configures TypeScript automatically. You can add multiple frameworks at once:
npx astro add react vue
Important
Each
astro addcommand modifiesastro.config.mjs. Review the changes after running it — especially if you already have a custom config — to ensure nothing was overwritten unexpectedly.
Adding adapters (for SSR)
To enable server-side rendering, install an adapter:
# Node.js (self-hosted)
npx astro add node
# Vercel
npx astro add vercel
# Netlify
npx astro add netlify
# Cloudflare (Pages / Workers)
npx astro add cloudflare
Then set output: "server" in your config (see the Configuration section).
Node.js version requirement
Astro requires Node.js 22.12.0 or higher. If you're using a version manager:
# nvm
nvm install 22
nvm use 22
# fnm
fnm install 22
fnm use 22
3. Configuration
Astro is configured via astro.config.mjs (or .ts, .cjs) at the project root.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
// your options here
});
defineConfig() provides full TypeScript IntelliSense — hover over any option in your editor for docs.
Tip
You can also use
astro.config.tsfor a fully-typed config file. TypeScript support is first-class and catches config errors at editor time, not build time.
Core options
| Option | Default | Description |
|---|---|---|
root |
. |
Project root directory |
srcDir |
./src |
Source directory |
publicDir |
./public |
Static assets directory |
outDir |
./dist |
Build output directory |
cacheDir |
./node_modules/.astro |
Build cache directory |
base |
/ |
Base path for deployment (e.g. /my-app) |
trailingSlash |
ignore |
URL trailing slash policy |
output |
static |
Rendering mode (static or server) |
compressHTML |
true |
Minify HTML output |
Server options
export default defineConfig({
server: {
host: false, // Set true to expose on LAN, or a specific IP
port: 4321, // Dev server port
open: false, // Auto-open browser on dev start
},
});
Pass --host to astro dev to expose your dev server on your local network (useful for testing on mobile devices).
Build options
export default defineConfig({
build: {
format: 'directory', // 'file' | 'directory' | 'preserve'
assets: '_astro', // Output folder for hashed assets
inlineStylesheets: 'auto', // 'always' | 'auto' | 'never'
concurrency: 1, // Parallel pages to build at once
redirects: true, // Emit redirect HTML files
serverEntry: 'entry.mjs', // Server entry point filename (SSR)
},
});
Increasing build.concurrency can speed up large static sites — experiment with values like 4 or 8 on multi-core machines.
Output modes
// Static site generation (default) — pre-renders all pages at build time
export default defineConfig({
output: 'static',
});
// Server-side rendering — requires an adapter
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
});
Warning
The
output: "hybrid"mode was removed in Astro v6. If you're migrating from v5 and usedhybrid, switch tooutput: "static"— it now covers the same use case. Individual pages can still opt into server rendering withexport const prerender = false.
Trailing slash
export default defineConfig({
trailingSlash: 'ignore', // Accept both /about and /about/
// trailingSlash: 'always', // Redirect to /about/
// trailingSlash: 'never', // Redirect to /about
});
Caution
If
trailingSlashis set toalways, API endpoints that don't end with/will return 404. Ensure your fetch calls and form actions use the trailing slash when this mode is active.
Integrations
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
integrations: [
react(),
mdx(),
sitemap(),
],
site: 'https://example.com', // Required for sitemap
});
Image options
export default defineConfig({
image: {
// Remote image domains to allow optimization
remotePatterns: [
{ protocol: 'https', hostname: '**.example.com' },
],
// Default image service (Sharp is the default)
service: {
entrypoint: 'astro/assets/services/sharp',
},
},
});
Security options
export default defineConfig({
security: {
checkOrigin: true, // CSRF protection (enabled by default)
actionBodySizeLimit: '1MB', // Max body size for Actions
serverIslandBodySizeLimit: '1MB', // Max body for Server Islands
},
});
Environment variable schema
import { defineConfig, envField } from 'astro/config';
export default defineConfig({
env: {
schema: {
PUBLIC_API_URL: envField.string({ context: 'client', access: 'public' }),
DATABASE_URL: envField.string({ context: 'server', access: 'secret' }),
PORT: envField.number({ context: 'server', access: 'public', default: 3000 }),
},
},
});
Variables declared here are validated at build time — missing required values fail the build immediately.
i18n routing
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'es', 'fr', 'de'],
fallback: {
es: 'en', // Fall back to English if Spanish page missing
},
routing: {
prefixDefaultLocale: false, // /en/about → /about
},
},
});
Prefetching
export default defineConfig({
prefetch: {
prefetchAll: false,
defaultStrategy: 'hover', // 'tap' | 'hover' | 'viewport' | 'load'
},
});
Fonts
export default defineConfig({
fonts: [
{
provider: 'google',
name: 'Inter',
weights: [400, 600, 700],
},
],
});
Scoped style strategy
export default defineConfig({
scopedStyleStrategy: 'attribute', // 'where' | 'class' | 'attribute'
});
attribute— adds adata-astro-*attribute (default, broadest browser support)class— adds a generated class namewhere— uses:where()(zero specificity impact)
See the Admin Guide for the complete configuration reference including all adapter-specific options, advanced Vite configuration, and integration authoring APIs.
4. Core Usage
The .astro component
Every Astro component is a .astro file with two sections separated by ---:
---
// Frontmatter: runs on the server (or at build time)
// Import components, fetch data, define variables
import Header from '../components/Header.astro';
const greeting = "Hello!";
const posts = await fetch('/api/posts').then(r => r.json());
---
<!-- Template: HTML with {expressions} -->
<html lang="en">
<head>
<title>{greeting}</title>
</head>
<body>
<Header />
<main>
<h1>{greeting}</h1>
<ul>
{posts.map(post => (
<li><a href={`/blog/${post.slug}`}>{post.title}</a></li>
))}
</ul>
</main>
</body>
</html>
<style>
/* Scoped to this component by default */
h1 { color: rebeccapurple; }
</style>
The frontmatter runs once at build time (static) or on every request (server mode). No JavaScript ships to the browser from this section.
File-based routing
Every file in src/pages/ becomes a route:
| File | URL |
|---|---|
src/pages/index.astro |
/ |
src/pages/about.astro |
/about |
src/pages/blog/index.astro |
/blog |
src/pages/blog/[slug].astro |
/blog/:slug |
src/pages/[...slug].astro |
/* (catch-all) |
src/pages/api/data.ts |
/api/data (endpoint) |
Dynamic routes
For static generation, export getStaticPaths():
---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { slug: post.id },
props: { post },
}));
}
const { post } = Astro.props;
---
<article>
<h1>{post.data.title}</h1>
<div set:html={post.rendered.html} />
</article>
Astro calls getStaticPaths() at build time and generates one HTML page per returned entry.
Props and component communication
---
// src/components/Card.astro
interface Props {
title: string;
description: string;
href?: string;
}
const { title, description, href = '#' } = Astro.props;
---
<div class="card">
<h2><a href={href}>{title}</a></h2>
<p>{description}</p>
</div>
Pass props like HTML attributes:
---
import Card from '../components/Card.astro';
---
<Card title="Getting Started" description="Learn the basics." href="/docs" />
Layouts
Layouts are .astro components that wrap page content using <slot />:
---
// src/layouts/Base.astro
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{title}</title>
</head>
<body>
<nav><!-- site nav --></nav>
<main>
<slot /> <!-- Page content goes here -->
</main>
<footer><!-- site footer --></footer>
</body>
</html>
Use in a page:
---
import Base from '../layouts/Base.astro';
---
<Base title="About Us">
<h1>About Us</h1>
<p>We build cool things.</p>
</Base>
Named slots
---
// src/layouts/TwoColumn.astro
---
<div class="layout">
<aside>
<slot name="sidebar" />
</aside>
<main>
<slot />
</main>
</div>
<TwoColumn>
<nav slot="sidebar">
<a href="/docs">Docs</a>
</nav>
<h1>Main content here</h1>
</TwoColumn>
CSS and styling
Styles in <style> tags are scoped to the component by default:
<h1>This heading is blue</h1>
<p>This paragraph is unstyled</p>
<style>
/* Only applies to h1 in THIS component */
h1 {
color: blue;
}
</style>
Global styles — opt out of scoping:
<style is:global>
/* Applies everywhere */
body {
margin: 0;
font-family: system-ui;
}
</style>
Inline styles — skip processing entirely:
<style is:inline>
/* Injected verbatim, no bundling */
@import url('https://fonts.googleapis.com/css2?family=Inter');
</style>
CSS variables from frontmatter:
---
const primaryColor = '#ff6b6b';
const fontSize = '1.5rem';
---
<h1 class="title">Hello</h1>
<style define:vars={{ primaryColor, fontSize }}>
.title {
color: var(--primaryColor);
font-size: var(--fontSize);
}
</style>
Import CSS files:
---
import '../styles/global.css';
---
Tailwind CSS:
npx astro add tailwind
JavaScript and TypeScript
Astro supports TypeScript natively — rename any .astro frontmatter or import files to .ts.
Client-side scripts run in the browser:
<button id="toggle">Toggle</button>
<script>
// This runs in the browser
const btn = document.getElementById('toggle');
btn?.addEventListener('click', () => {
document.body.classList.toggle('dark');
});
</script>
Scripts are bundled and deduplicated. A <script> in a component used 50 times is only included once.
External script files:
<script src="/scripts/analytics.js" is:inline></script>
Markdown and MDX
Any .md file in src/pages/ becomes a page:
---
title: My Blog Post
date: 2026-01-15
---
# My Blog Post
This is markdown content with **bold**, _italic_, and `code`.
For .mdx files (Markdown + JSX components), install the MDX integration:
npx astro add mdx
Then in .mdx files:
---
title: Interactive Post
---
import Counter from '../components/Counter.jsx';
# Interactive Post
This post has a live component:
<Counter client:load />
And regular markdown continues here.
API endpoints
Create .ts (or .js) files in src/pages/ that export HTTP method handlers:
// src/pages/api/hello.ts
import type { APIRoute } from 'astro';
export const GET: APIRoute = ({ params, request }) => {
return new Response(JSON.stringify({ message: 'Hello!' }), {
headers: { 'Content-Type': 'application/json' },
});
};
export const POST: APIRoute = async ({ request }) => {
const body = await request.json();
return new Response(JSON.stringify({ received: body }));
};
UI framework components (Islands)
Install a framework integration, then use components in .astro files:
---
import ReactCounter from '../components/Counter.jsx';
import VueWidget from '../components/Widget.vue';
import SvelteModal from '../components/Modal.svelte';
---
<!-- No client directive = server render only, zero JS -->
<ReactCounter initialCount={0} />
<!-- client:load = hydrate immediately -->
<ReactCounter client:load initialCount={0} />
<!-- client:idle = hydrate when browser is idle -->
<VueWidget client:idle />
<!-- client:visible = hydrate when scrolled into view -->
<SvelteModal client:visible />
<!-- client:media = hydrate when media query matches -->
<VueWidget client:media="(max-width: 768px)" />
<!-- client:only = skip SSR, render client-side only -->
<ReactCounter client:only="react" initialCount={0} />
Client directives reference
| Directive | When it hydrates | Use case |
|---|---|---|
client:load |
Immediately on page load | Above-the-fold interactive elements |
client:idle |
When browser is idle (requestIdleCallback) | Non-critical widgets |
client:visible |
When entering the viewport | Below-the-fold components |
client:media="(query)" |
When CSS media query matches | Mobile-only or desktop-only UI |
client:only="framework" |
Client-side only (no SSR) | Browser-API-dependent components |
client:idle accepts an optional timeout: client:idle={{ timeout: 500 }} (milliseconds).
Note
Omitting a
client:*directive renders the component to static HTML at build time (or on the server for SSR). The component's JavaScript never ships to the browser. This is the default and the key to Astro's performance story.
Astro global object
In .astro frontmatter and components, Astro is available globally:
---
// Request info (SSR)
const url = Astro.url; // URL object
const params = Astro.params; // Dynamic route params
const props = Astro.props; // Component props
// Redirect (SSR)
if (!loggedIn) {
return Astro.redirect('/login');
}
// Cookies (SSR)
const theme = Astro.cookies.get('theme')?.value ?? 'light';
Astro.cookies.set('theme', 'dark', { maxAge: 86400 });
// Site info
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
// Locals (set by middleware)
const user = Astro.locals.user;
---
5. Common Tasks
Task 1: Build a blog with content collections
Step 1: Define your collection schema in src/content/config.ts:
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content', // .md / .mdx files
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
author: z.string().default('Anonymous'),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
heroImage: z.string().optional(),
}),
});
export const collections = { blog };
Step 2: Create posts in src/content/blog/:
---
title: "My First Post"
description: "Getting started with Astro blogging."
pubDate: 2026-01-15
author: "Jane Doe"
tags: ["astro", "web"]
---
# My First Post
Welcome to my blog! This is written in Markdown...
Step 3: Generate TypeScript types:
npx astro sync
Step 4: List posts:
---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => !data.draft);
const sorted = posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---
<ul>
{sorted.map(post => (
<li>
<a href={`/blog/${post.id}`}>{post.data.title}</a>
<time>{post.data.pubDate.toLocaleDateString()}</time>
</li>
))}
</ul>
Step 5: Render individual posts:
---
// src/pages/blog/[slug].astro
import { getCollection, render } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog', ({ data }) => !data.draft);
return posts.map(post => ({
params: { slug: post.id },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<article>
<h1>{post.data.title}</h1>
<time>{post.data.pubDate.toLocaleDateString()}</time>
<Content />
</article>
Task 2: Add a contact form with Actions
Actions are type-safe server functions that handle form submissions and other mutations.
Step 1: Define an action in src/actions/index.ts:
import { defineAction } from 'astro:actions';
import { z } from 'astro:schema';
export const server = {
contact: defineAction({
accept: 'form',
input: z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email'),
message: z.string().min(10, 'Message must be at least 10 characters'),
}),
handler: async ({ name, email, message }) => {
// Send email, save to DB, etc.
await sendEmail({ to: '[email removed]', name, email, message });
return { success: true };
},
}),
};
Step 2: Use the action in a form:
---
import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.contact);
---
{result?.error && (
<p class="error">{result.error.message}</p>
)}
{result?.data?.success && (
<p class="success">Message sent! We'll be in touch.</p>
)}
<form method="POST" action={actions.contact}>
<label>
Name
<input type="text" name="name" required />
</label>
<label>
Email
<input type="email" name="email" required />
</label>
<label>
Message
<textarea name="message" rows="5" required></textarea>
</label>
<button type="submit">Send</button>
</form>
Tip
Actions work with both form submissions (
accept: 'form') and JSON requests. For progressive enhancement, the form works without JavaScript because it uses a standard POST — the action enriches it with validation and type safety.
Task 3: Optimize images
Astro's built-in <Image> component automatically optimizes images:
---
import { Image, Picture } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---
<!-- Optimized image: resized, converted to WebP, lazy-loaded -->
<Image
src={heroImage}
alt="A scenic mountain landscape"
width={1200}
height={600}
/>
<!-- Responsive image with multiple formats -->
<Picture
src={heroImage}
alt="A scenic mountain landscape"
widths={[400, 800, 1200]}
formats={['avif', 'webp', 'jpeg']}
sizes="(max-width: 800px) 100vw, 800px"
/>
For remote images, declare allowed domains first:
// astro.config.mjs
export default defineConfig({
image: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.unsplash.com' },
{ protocol: 'https', hostname: '**.cloudinary.com' },
],
},
});
Then use remote URLs:
<Image
src="https://images.unsplash.com/photo-xyz?w=1200"
alt="Remote image"
width={1200}
height={800}
inferSize={true}
/>
Task 4: Add SSR with an adapter
Install an adapter:
npx astro add node
Set output mode:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
});
Build and run:
npm run build
node dist/server/entry.mjs
Mix static and dynamic pages:
Even in server mode, pages can pre-render by opting in. Opt into on-demand rendering per page:
---
// This page renders on every request
export const prerender = false;
const user = await getUser(Astro.cookies.get('session')?.value);
if (!user) return Astro.redirect('/login');
---
<h1>Dashboard for {user.name}</h1>
Or in static mode, opt specific pages into server rendering:
---
export const prerender = false; // Works in static mode too
---
Task 5: Protect routes with middleware
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(async (context, next) => {
const { pathname } = context.url;
// Protect /dashboard routes
if (pathname.startsWith('/dashboard')) {
const session = context.cookies.get('session')?.value;
if (!session) {
return context.redirect('/login');
}
// Attach user to locals for downstream components
context.locals.user = await getUserFromSession(session);
}
return next();
});
Chain multiple middleware:
import { sequence } from 'astro:middleware';
import { auth } from './middleware/auth';
import { logger } from './middleware/logger';
import { i18n } from './middleware/i18n';
export const onRequest = sequence(logger, i18n, auth);
Task 6: Set up i18n routing
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'es', 'fr'],
routing: {
prefixDefaultLocale: false, // English at /, Spanish at /es/
},
fallback: {
es: 'en', // Spanish falls back to English if page missing
fr: 'en',
},
},
});
Organize pages by locale:
src/pages/
├── index.astro → /
├── about.astro → /about
├── es/
│ ├── index.astro → /es/
│ └── about.astro → /es/about
└── fr/
└── index.astro → /fr/
Use i18n helpers:
---
import { getRelativeLocaleUrl } from 'astro:i18n';
const locale = Astro.currentLocale ?? 'en';
---
<nav>
<a href={getRelativeLocaleUrl(locale, '/')}>Home</a>
<a href={getRelativeLocaleUrl(locale, '/about')}>About</a>
</nav>
Task 7: Generate an RSS feed
npm install @astrojs/rss
// src/pages/rss.xml.ts
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
import type { APIContext } from 'astro';
export async function GET(context: APIContext) {
const posts = await getCollection('blog');
return rss({
title: 'My Blog',
description: 'A collection of posts about web development.',
site: context.site!,
items: posts.map(post => ({
title: post.data.title,
pubDate: post.data.pubDate,
description: post.data.description,
link: `/blog/${post.id}/`,
})),
customData: `<language>en-us</language>`,
});
}
Task 8: Generate a sitemap
npx astro add sitemap
Set your site URL (required):
export default defineConfig({
site: 'https://example.com',
integrations: [sitemap()],
});
The sitemap is generated automatically at /sitemap-index.xml on each build.
Task 9: Use environment variables
Declare your schema in config:
// astro.config.mjs
import { defineConfig, envField } from 'astro/config';
export default defineConfig({
env: {
schema: {
PUBLIC_API_URL: envField.string({
context: 'client',
access: 'public',
}),
SECRET_KEY: envField.string({
context: 'server',
access: 'secret',
}),
FEATURE_FLAGS: envField.boolean({
context: 'server',
access: 'public',
default: false,
}),
},
},
});
Use validated env vars:
// Server-side (frontmatter, API routes, middleware)
import { SECRET_KEY, FEATURE_FLAGS } from 'astro:env/server';
// Client-side (browser scripts only)
import { PUBLIC_API_URL } from 'astro:env/client';
Important
Only variables declared with
context: 'client'are available in browser scripts. Never importastro:env/serverin client-side code — it will throw at runtime. Missing required variables cause the build to fail immediately with a clear error.
Task 10: Add prefetching
Enable prefetching for faster page transitions:
export default defineConfig({
prefetch: true, // Use default strategy (hover)
// Or configure:
prefetch: {
prefetchAll: false,
defaultStrategy: 'hover', // 'tap' | 'hover' | 'viewport' | 'load'
},
});
Prefetch specific links:
<!-- Prefetch on hover (default when prefetch is enabled) -->
<a href="/about" data-astro-prefetch>About</a>
<!-- Override strategy per-link -->
<a href="/popular-page" data-astro-prefetch="viewport">Popular</a>
<a href="/slow-page" data-astro-prefetch="load">Heavy Page</a>
Task 11: Use Server Islands for deferred content
Server Islands let you defer slow server-rendered sections — the page shell loads instantly, then the island fetches separately.
---
import UserGreeting from '../components/UserGreeting.astro';
import ProductGrid from '../components/ProductGrid.astro';
---
<!-- Page loads instantly, these render separately -->
<UserGreeting server:defer>
<!-- Fallback shown while loading -->
<div slot="fallback">Loading personalized content...</div>
</UserGreeting>
<ProductGrid server:defer>
<div slot="fallback" class="skeleton">Loading products...</div>
</ProductGrid>
Note
Server Islands require an adapter (SSR mode). The
server:deferdirective tells Astro to stream the page immediately and fetch the island's content in a separate request. This is ideal for personalized content — user greetings, cart counts — that would otherwise block the entire page.
Task 12: Set up sessions
Sessions require an adapter and an encryption key.
Generate a key:
npx astro create-key
Add the output to your .env:
ASTRO_KEY=<generated-key-here>
Use sessions in pages:
---
export const prerender = false;
const session = Astro.session;
// Read
const cart = await session?.get('cart') ?? [];
// Write
await session?.set('cart', [...cart, newItem]);
// Destroy
await session?.destroy();
---
Task 13: Configure redirects
Static redirects in config (no runtime overhead):
export default defineConfig({
redirects: {
'/old-blog': '/blog',
'/old-post': {
destination: '/blog/new-post',
status: 301, // Permanent redirect
},
'/docs/[...slug]': '/reference/[...slug]', // Dynamic redirect
},
});
Task 14: Add MDX with custom components
npx astro add mdx
Pass custom components to MDX content:
---
import { Content } from '../content/blog/my-post.mdx';
import CodeBlock from '../components/CodeBlock.astro';
import Callout from '../components/Callout.astro';
---
<Content
components={{
pre: CodeBlock, // Replace <pre> with custom component
blockquote: Callout, // Replace <blockquote>
}}
/>
Task 15: Use Markdoc for structured content
npx astro add markdoc
Markdoc provides a more constrained, validatable authoring experience compared to MDX — ideal for docs sites and content teams that shouldn't have arbitrary JSX access. Define custom tags and nodes to control exactly what authors can embed.
Task 16: Load external data with the Content Layer
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const products = defineCollection({
type: 'content_layer',
loader: async () => {
const response = await fetch('https://api.example.com/products');
const data = await response.json();
return data.map((product: any) => ({
id: product.slug,
...product,
}));
},
schema: z.object({
name: z.string(),
price: z.number(),
inStock: z.boolean(),
}),
});
export const collections = { products };
Query it like any other collection:
---
import { getCollection } from 'astro:content';
const products = await getCollection('products', ({ data }) => data.inStock);
---
Task 17: Deploy to Vercel
npx astro add vercel
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';
export default defineConfig({
output: 'server',
adapter: vercel(),
});
Push to GitHub and connect the repo in the Vercel dashboard. Set output: "server" only if you need SSR — static sites deploy to Vercel without any adapter.
Task 18: View current environment and debug info
# Print system info for bug reports
npx astro info
# Copy to clipboard
npx astro info --copy
# Verbose build (see what's happening)
npx astro build --verbose
# Open the official docs
npx astro docs
6. Troubleshooting
Dev server issues
Dev server won't start — port in use
# Use a different port
npx astro dev --port 3000
# Or find and kill the process on 4321 (on Unix)
npx kill-port 4321
TypeError: Cannot read properties of undefined (reading 'validateOptions') in assets/internal.js during pnpm run dev (issue #16309)
This error occurs in some pnpm environments. Try:
- Clear the Astro cache:
rm -rf node_modules/.astro - Reinstall dependencies:
pnpm install --force - If using a custom image service, verify its
validateOptionsexport matches the expected signature.
Components not rendered on page refresh using dev server (issue #15925)
If some components don't appear after a hard refresh but show on navigation, this is a known dev-server issue. Workaround:
- Stop the dev server.
- Clear
.astrocache:rm -rf node_modules/.astro - Restart:
npx astro dev
astro dev starts but browser shows blank page
- Check the terminal for build errors — a syntax error in any imported file causes a blank output.
- Open DevTools console for client-side errors.
- Ensure
src/pages/index.astro(orindex.html) exists.
Dev server not reflecting file changes
Clear cache and restart:
rm -rf node_modules/.astro
npx astro dev
Build issues
Build fails on Netlify / Vercel (Vite build fail) (issue #16196)
If you hit a Vite build failure on CI/CD:
- Ensure your adapter is installed:
npx astro add netlifyornpx astro add vercel - Verify
output: "server"is set when using an adapter - Check for missing environment variables in your deployment settings
- Try clearing the build cache in your CI platform's settings
CSS missing in production when using export const partial = true (issue #16296)
Pages with export const partial = true may lose their styles in production builds. Workaround: avoid using partial = true for pages that have associated CSS, or move styles to a global stylesheet.
Build error: hybrid output mode not found
The hybrid output mode was removed in Astro v6. Change your config:
// Before (v5)
export default defineConfig({ output: 'hybrid' });
// After (v6)
export default defineConfig({ output: 'static' });
// Individual pages opt into SSR with: export const prerender = false;
TypeScript errors in .astro files
Run diagnostics:
npx astro check
For watch mode during development:
npx astro check --watch
If you see "types not found" for content collections, regenerate them:
npx astro sync
Cannot find module 'astro:content' or astro:actions
These virtual modules require astro sync to be run first. The dev server runs it automatically, but CI environments may need:
npx astro sync && npx astro build
Styling issues
Vue <style scoped> breaks after ClientRouter navigation (issue #16334)
Vue scoped styles can lose their scoping after soft navigation in dev mode when using ClientRouter. Notes:
- In production builds, the issue does not occur.
- In dev mode, use
npx astro devand do a hard refresh (Ctrl+Shift+R) to reset scoping. - Alternatively, use Astro's own scoped styles instead of Vue's for affected components.
Vue components with :deep() styles not applied in dev (issue #15898)
:deep() combinators in Vue components may not apply during dev when ClientRouter is active. Workaround: move deep styles to a global stylesheet or into an Astro component's <style is:global>.
Styles not applying at all in a component
Check:
- Is the component being used with a
client:onlydirective? If so, the component renders client-side only — Astro scoped styles won't reach it. Use the framework's own styling. - Is specificity fighting you? Use
scopedStyleStrategy: 'where'in config to reduce scoped style specificity to zero. - Are you importing CSS from
node_modules? Import it in a layout's<style is:global>or via Vite config.
Unable to load UnoCSS styles with @apply (issue #16373)
UnoCSS's @apply directive can fail to resolve in Astro's Vite pipeline. Workarounds:
- Use UnoCSS's
uno.cssimport approach instead of@apply - Ensure your
viteconfig inastro.config.mjsincludes UnoCSS as a Vite plugin (not just a PostCSS plugin) - Check UnoCSS docs for Astro-specific integration notes
Routing issues
404 on all pages except index
Ensure your hosting platform serves Astro's output correctly. For the Node adapter in standalone mode, run node dist/server/entry.mjs. For static sites, the dist/ folder should be the webroot — configure your server to serve index.html for directory paths.
trailingSlash: always causes 404 on endpoints (issue #16338)
When trailingSlash is always, API endpoints like /api/data become /api/data/ — update your fetch calls to match:
// This will 404 with trailingSlash: 'always'
const res = await fetch('/api/data');
// Use trailing slash
const res = await fetch('/api/data/');
Or switch to trailingSlash: 'ignore' if you don't need the redirect behavior.
Dynamic route params not working
Ensure the filename matches the param name:
[slug].astro→Astro.params.slug[...slug].astro→Astro.params.slug(captures entire path segment)
For static sites, ensure getStaticPaths() returns all possible values.
Redirect loops
If middleware redirects unconditionally, it will loop. Always add a condition:
export const onRequest = defineMiddleware(async (context, next) => {
const { pathname } = context.url;
// Exclude the login page from auth check
if (pathname.startsWith('/dashboard') && pathname !== '/login') {
// check auth...
}
return next();
});
Content collections issues
getCollection() returns entries in different order than expected (issue #16306)
In Astro v6, getCollection() returns entries in filesystem order, which may differ from v5's behavior. Sort explicitly:
const posts = await getCollection('blog');
const sorted = posts.sort((a, b) =>
b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
Never rely on getCollection()'s default order for display purposes.
Content collection schema validation failing
Run astro sync after changing schemas. Errors appear during the sync step and identify exactly which files fail validation.
render() returns undefined for content items
The render() function (imported from astro:content) only works with type: 'content' collections (.md/.mdx files). For type: 'data' collections (.json/.yaml), use the data directly without rendering.
Image issues
Remote images failing — "hostname not allowed"
Add the domain to image.remotePatterns:
export default defineConfig({
image: {
remotePatterns: [
{ protocol: 'https', hostname: 'your-cdn.com' },
],
},
});
Sharp not found / image optimization disabled
Install Sharp explicitly:
npm install sharp
On some platforms (Vercel, etc.), Sharp is bundled automatically. If you see warnings about the fallback image service being used, it means Sharp isn't installed in your environment.
<Image> component throwing TypeScript errors about width/height
Both width and height are required for remote images. For local images, they're inferred automatically from the imported asset. If you need to override, pass both explicitly.
Integration issues
decap-cms-app@^3.12 import error (issue #16368)
This CMS package uses CJS-only internals that conflict with Astro's ESM pipeline. Workaround:
// astro.config.mjs
export default defineConfig({
vite: {
ssr: {
noExternal: ['decap-cms-app'],
},
},
});
If that doesn't resolve it, import Decap CMS in a client:only component so it never runs through Astro's server pipeline.
astro add fails with permission errors
Run with explicit package manager:
npm exec astro add react
# or
pnpm dlx astro add react
Integration modifies config incorrectly
After running astro add, always review the diff to astro.config.mjs. If something looks wrong, restore from git and add the integration manually following its README.
Markdown / MDX issues
Syntax highlighting not working
Astro uses Shiki by default. If highlighting is missing:
- Ensure your code blocks have a language tag (
```javascriptnot just```) - Shiki supports hundreds of languages — check the Shiki language list if yours isn't highlighting
- To switch to Prism:
npm install prism-themesand configure inastro.config.mjsundermarkdown.syntaxHighlight: 'prism'
Remark/rehype plugin not working
// astro.config.mjs
import remarkGfm from 'remark-gfm';
import rehypeSlug from 'rehype-slug';
export default defineConfig({
markdown: {
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeSlug],
},
});
Ensure plugins are in remarkPlugins (not rehypePlugins) and vice versa. Remark operates on the Markdown AST; Rehype operates on the HTML AST.
Actions issues
Action handler not called
Verify:
- The form
actionattribute uses the imported action reference, not a string path. - The form method is
POST. - Actions require a server context — ensure you're not on a fully static page without
export const prerender = false.
Zod validation errors not showing
Use Astro.getActionResult() to retrieve the result:
---
const result = Astro.getActionResult(actions.myAction);
---
{result?.error?.fieldErrors?.email && (
<p class="error">{result.error.fieldErrors.email[0]}</p>
)}
Session issues
Sessions not persisting
- Ensure
ASTRO_KEYis set in your environment (generated byastro create-key). - Sessions require an adapter. Check that
output: "server"is configured. - Verify cookies are being sent — check browser DevTools → Application → Cookies.
astro create-key output format
The command outputs a key in the format needed for your .env file directly. Copy the entire output line.
Performance issues
Large build taking too long
Increase build concurrency:
export default defineConfig({
build: {
concurrency: 4, // or 8 on powerful machines
},
});
Large JavaScript bundle sizes
Check which components have client:load — these ship JS to the browser. Consider switching to client:idle or client:visible for non-critical components. Use npx astro build --verbose to see what's being bundled.
Dev server slow to start
Large src/content/ directories with many files slow down the initial sync. Run astro sync once to cache types, then subsequent starts are faster.
7. FAQ
General
Q: What makes Astro different from Next.js or Nuxt?
Astro ships zero JavaScript by default. Next.js and Nuxt ship a full React/Vue runtime to every visitor, even for mostly-static pages. In Astro, JavaScript only ships when you explicitly request it via client:* directives. This makes Astro exceptionally fast for content sites — blogs, docs, marketing pages — while also being capable of building fully interactive apps.
Q: Is Astro only for static sites?
No. Astro supports full server-side rendering via adapters (@astrojs/node, @astrojs/vercel, @astrojs/netlify, @astrojs/cloudflare). Set output: "server" in your config and install an adapter. You can also mix static and dynamic pages in the same project.
Q: What is the "Islands Architecture"?
It's Astro's mental model for hydration. A page is a "sea" of static HTML. Interactive components are "islands" — isolated, independently hydrating units. You control exactly which components hydrate and when, using client:* directives. Islands don't share state or bundle with each other by default.
Q: Can I use React AND Vue in the same project?
Yes. Run npx astro add react vue and use both. Each framework's runtime is loaded independently. You can even nest a Vue component inside a React component and vice versa — though sharing state between them requires a framework-agnostic store (like nanostores).
Q: Does Astro support TypeScript?
Yes, natively. TypeScript works in .astro frontmatter, .ts files, and framework components. Run npx astro check to type-check your project, or use npx astro check --watch during development.
Q: What is the minimum Node.js version?
Node.js 22.12.0 or higher. Astro uses modern Node.js APIs and will not run on older versions.
Q: Is Astro production-ready?
Yes. Astro is used by large organizations for production documentation sites, marketing sites, e-commerce, and more. The framework has a stable v6 release with active maintenance.
Q: What happened to output: "hybrid"?
The hybrid mode was removed in Astro v6. Replace it with output: "static" — the current static mode supports per-page server rendering via export const prerender = false, which was previously the unique feature of hybrid mode.
Content Collections
Q: What is a content collection?
A content collection is a folder in src/content/ with a defined schema. Astro validates every file against the schema and generates TypeScript types automatically. You query collections with getCollection() and getEntry() from astro:content.
Q: What's the difference between type: 'content' and type: 'data'?
type: 'content'— for.mdand.mdxfiles with frontmatter. You get arender()function to produce HTML.type: 'data'— for.jsonand.yamlfiles. Pure structured data, no rendering.
Q: What is the Content Layer?
The Content Layer (type: 'content_layer') extends collections to load data from any external source — an API, a CMS, a database. You provide an async loader function; Astro caches the result between builds.
Q: Do I need to run astro sync manually?
The dev server runs astro sync automatically. For production builds and CI, add it to your build command: astro sync && astro build. It generates the TypeScript types that astro:content imports rely on.
Q: Can I use Zod for validation in content schemas?
Yes. Astro exports Zod from astro:content (import { z } from 'astro:content') — use the full Zod API for schemas. You can also import Zod directly from astro:schema in Actions.
Q: How do I query content by a tag or category?
Pass a filter function to getCollection():
const reactPosts = await getCollection('blog', ({ data }) =>
data.tags.includes('react')
);
Q: Can I paginate content collection results?
Yes, use Astro's paginate() helper in getStaticPaths():
---
export async function getStaticPaths({ paginate }) {
const posts = await getCollection('blog');
return paginate(posts, { pageSize: 10 });
}
const { page } = Astro.props;
---
Components and Rendering
Q: What's the difference between .astro and framework components?
.astro components always render server-side (or at build time) — no JavaScript ships to the browser. Framework components (React, Vue, etc.) also render server-side by default, but you can add client:* directives to hydrate them. Use .astro components for layout, navigation, and anything that doesn't need interactivity. Use framework components when you need client-side state.
Q: Can I share state between Astro islands?
Not directly — each island hydrates independently. Use a framework-agnostic store like nanostores for cross-island state:
// store.js
import { atom } from 'nanostores';
export const cartCount = atom(0);
// CartCounter.jsx (React island)
import { useStore } from '@nanostores/react';
import { cartCount } from '../store';
export function CartCounter() {
const count = useStore(cartCount);
return <span>{count} items</span>;
}
Q: When should I use client:only?
Use client:only for components that use browser-only APIs (window, localStorage, navigator) and cannot run server-side at all. Examples: third-party map SDKs, browser-specific charts, components that depend on document. Always pass the framework name: client:only="react".
Q: Can .astro components use React hooks?
No. Hooks are a React concept and only work in React components. Astro's frontmatter is plain JavaScript/TypeScript that runs at build/request time.
Q: How does Astro handle component CSS specificity?
By default (scopedStyleStrategy: 'attribute'), Astro adds a data-astro-* attribute to scoped elements and styles target that attribute. This adds one attribute to the specificity chain. Use scopedStyleStrategy: 'where' for zero-specificity scoping (easier to override with global styles).
Q: Can I use CSS Modules in Astro?
Yes. Import .module.css files:
---
import styles from './Button.module.css';
---
<button class={styles.button}>Click</button>
Q: Can I use Tailwind with Astro?
Yes. Run npx astro add tailwind — it installs Tailwind and configures the integration automatically.
Routing
Q: How do dynamic routes work in static mode?
Export getStaticPaths() from any page with [param] in its filename. Astro calls this function at build time, collects all param values, and generates one page per entry. Any slug not returned by getStaticPaths() won't exist in the build.
Q: How do I create a catch-all route?
Name the file [...slug].astro. The slug captures everything: /docs/a/b/c → params.slug = 'a/b/c'.
Q: Can I redirect based on user data (e.g., authentication)?
Yes, in SSR mode. Use middleware to check cookies/headers and call context.redirect(). In static mode, client-side redirects are possible via JavaScript, but server-side redirects require SSR.
Q: How do I add a 404 page?
Create src/pages/404.astro. Astro automatically uses it for unmatched routes.
Q: Can I have both static and server-rendered pages?
Yes. In static mode, add export const prerender = false to pages that need server rendering. In server mode, add export const prerender = true to pages that can be pre-rendered.
Server-Side Rendering
Q: Which adapter should I use?
| Platform | Adapter |
|---|---|
| Self-hosted Node.js | @astrojs/node |
| Vercel | @astrojs/vercel |
| Netlify | @astrojs/netlify |
| Cloudflare Pages/Workers | @astrojs/cloudflare |
Q: Can I access request headers in SSR?
Yes. In .astro frontmatter or API routes:
---
const authHeader = Astro.request.headers.get('Authorization');
---
Q: How do cookies work?
Use Astro.cookies in .astro components or context.cookies in middleware and API routes:
// Get a cookie
const theme = Astro.cookies.get('theme')?.value;
// Set a cookie
Astro.cookies.set('theme', 'dark', {
maxAge: 60 * 60 * 24 * 365, // 1 year
httpOnly: true,
secure: true,
sameSite: 'strict',
});
// Delete a cookie
Astro.cookies.delete('theme');
Middleware
Q: When does middleware run?
Middleware runs on every request before the page or endpoint handler. In SSR mode this is every HTTP request. In static mode, middleware doesn't run at build time — you need SSR or a hosting platform that supports edge middleware.
Q: How do I pass data from middleware to pages?
Use context.locals:
// middleware.ts
context.locals.user = await getUser(context.cookies.get('session')?.value);
// page.astro
const { user } = Astro.locals;
Declare types for locals in src/env.d.ts:
declare namespace App {
interface Locals {
user: { id: string; name: string } | null;
}
}
Actions
Q: What are Actions for?
Actions are type-safe server functions for mutations — form submissions, data writes, API calls that change state. They're similar to server actions in Next.js or Remix's actions, but with Zod validation built in and full TypeScript inference.
Q: Do Actions require JavaScript in the browser?
No — with accept: 'form', actions work as standard HTML form POSTs and function without JavaScript. They progressively enhance to use fetch() when JavaScript is available.
Q: Can Actions call a database?
Yes. Actions run server-side. Import your database client, ORM, or any server-only module.
Images
Q: Do I need to use <Image> for all images?
No — the standard <img> tag works fine. Use <Image> when you want automatic optimization (format conversion to WebP/AVIF, resizing, lazy loading). For decorative or background images, <img> or CSS is fine.
Q: Can I optimize images from a CMS or CDN?
Yes. Add the hostname to image.remotePatterns and use <Image src="https://..." />. Astro will proxy the image through its optimization endpoint at /_image.
Q: Is Sharp required?
Sharp is the default optimizer and handles local images. Without it, Astro falls back to a passthrough service (no optimization). Install Sharp explicitly with npm install sharp.
Performance
Q: How do I measure my Astro site's performance?
Use Lighthouse (built into Chrome DevTools), WebPageTest, or PageSpeed Insights. Astro sites typically score 95-100 on Lighthouse out of the box for static content. Use the Dev Toolbar's audit feature during development to catch common issues early.
Q: What is the Dev Toolbar?
The Dev Toolbar is an overlay available during astro dev (enabled by default). It shows component boundaries, runs accessibility audits, flags performance issues, and can be extended by integrations. Disable it with devToolbar: { enabled: false } in config if it's distracting.
Q: Does Astro support view transitions?
Yes, via the <ClientRouter /> component. Add it to your layout to enable smooth page transitions with the View Transitions API. Note that Vue scoped styles and some CSS-in-JS patterns have known edge cases with ClientRouter in dev mode (see the Troubleshooting section).
Deployment
Q: How do I deploy a static Astro site?
Build with npm run build. Upload the dist/ folder to any static hosting:
- Vercel / Netlify: connect your GitHub repo — auto-detects Astro
- GitHub Pages: use the official GitHub Actions workflow from Astro docs
- Cloudflare Pages: connect repo, set build command
astro build, outputdist - Any CDN/host: upload
dist/contents to your webroot
Q: What's astro preview for?
It serves the production build (dist/) locally so you can verify the build before deploying. It's not meant for production use — use a proper web server or CDN for that.
Q: Can I run Astro in Docker?
Yes. Use the Node adapter and build a container:
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
EXPOSE 4321
CMD ["node", "dist/server/entry.mjs"]
Integrations and Ecosystem
Q: How do I find third-party integrations?
Visit astro.build/integrations — the official integration directory lists hundreds of community and official integrations.
Q: Can I use Vite plugins?
Yes. Pass them via vite.plugins in your config:
import { defineConfig } from 'astro/config';
import glsl from 'vite-plugin-glsl';
export default defineConfig({
vite: {
plugins: [glsl()],
},
});
Q: What is @astrojs/check?
It's the package behind astro check — the TypeScript diagnostics tool for .astro files. It's included as a dependency when you install Astro. Run it standalone with npx astro-check for CI integration.
Q: What is Astro DB?
Astro DB (@astrojs/db) is a managed SQLite-based database service built for Astro. Install it with npx astro add db. It provides a schema-first, TypeScript-native data layer with a local development database and cloud sync. Run database commands via npx astro db.
CLI
Q: What does astro info print?
System info for bug reports: Astro version, adapter, integrations, Node.js version, package manager, and OS. Use --copy to copy it to your clipboard before filing an issue.
Q: What does astro preferences do?
Manages per-user preferences stored locally (not in the project). Includes settings like whether to show the Dev Toolbar notification dot.
Q: How do I disable telemetry?
npx astro telemetry disable
Telemetry is anonymous and covers usage metrics. Disable it at any time — it does not affect functionality.
Complete CLI Reference
Commands
| Command | Description |
|---|---|
astro dev |
Start development server (Vite-powered, default port 4321) |
astro build |
Production build |
astro preview |
Preview production build locally |
astro check |
Run TypeScript diagnostics on .astro files |
astro sync |
Generate TypeScript types for content collections |
astro add <integration> |
Add and configure an integration |
astro info |
Print debug/system info for bug reports |
astro docs |
Open docs.astro.build in your browser |
astro preferences |
Manage local user preferences |
astro telemetry |
Enable or disable anonymous telemetry |
astro create-key |
Generate encryption key for sessions/server islands |
astro db |
Run Astro DB commands |
Global flags
| Flag | Default | Description |
|---|---|---|
--config <path> |
auto-detected | Path to config file |
--root <path> |
. |
Project root directory |
--site <url> |
— | Override site config value |
--base <path> |
/ |
Override base config value |
--verbose |
false | Enable debug-level logging |
--silent |
false | Suppress all output |
astro dev flags
| Flag | Default | Description |
|---|---|---|
--port <number> |
4321 |
Dev server port |
--host [string|bool] |
false |
Expose on network (true = all interfaces) |
--open [string|bool] |
false |
Open browser on start |
--allowedHosts <hosts> |
— | Comma-separated allowed hostnames |
astro build flags
| Flag | Default | Description |
|---|---|---|
--outDir <path> |
./dist |
Output directory |
--force |
false | Force rebuild, bypass cache |
--verbose |
false | Verbose output |
astro check flags
| Flag | Description |
|---|---|
--watch |
Re-run on file changes |
astro info flags
| Flag | Description |
|---|---|
--copy |
Copy output to clipboard |
Official Integrations Reference
Framework Renderers
| Package | Install command | Framework |
|---|---|---|
@astrojs/react |
npx astro add react |
React 18+ |
@astrojs/vue |
npx astro add vue |
Vue 3 |
@astrojs/svelte |
npx astro add svelte |
Svelte 4/5 |
@astrojs/preact |
npx astro add preact |
Preact |
@astrojs/solid-js |
npx astro add solid-js |
Solid.js |
@astrojs/alpinejs |
npx astro add alpinejs |
Alpine.js |
Adapters (SSR)
| Package | Install command | Platform |
|---|---|---|
@astrojs/node |
npx astro add node |
Node.js (self-hosted) |
@astrojs/vercel |
npx astro add vercel |
Vercel |
@astrojs/netlify |
npx astro add netlify |
Netlify |
@astrojs/cloudflare |
npx astro add cloudflare |
Cloudflare Pages/Workers |
Content
| Package | Install command | Purpose |
|---|---|---|
@astrojs/mdx |
npx astro add mdx |
MDX (Markdown + JSX) |
@astrojs/markdoc |
npx astro add markdoc |
Markdoc (structured content) |
Utilities
| Package | Install command | Purpose |
|---|---|---|
@astrojs/sitemap |
npx astro add sitemap |
Auto-generate XML sitemap |
@astrojs/partytown |
npx astro add partytown |
Off-main-thread third-party scripts |
@astrojs/db |
npx astro add db |
Astro DB (managed SQLite) |
@astrojs/rss |
npm install @astrojs/rss |
RSS/Atom feed generation |
@astrojs/check |
bundled | TypeScript diagnostics |
See the Admin Guide for architecture internals, adapter configuration deep-dives, build pipeline details, security hardening, and the complete configuration schema.