# Astro — User Guide

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 --version` before starting. Older Node versions will cause subtle build failures.

---

## 1. Quick Start

Get a project running in under two minutes.

```bash
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:

```bash
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) to `astro dev`. You can also set `server.port` in `astro.config.mjs` to 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)

```text
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)

```bash
# 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:

```bash
mkdir my-project && cd my-project
npm init -y
npm install astro
```

Add scripts to `package.json`:

```json
{
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview"
  }
}
```

Create `src/pages/index.astro`:

```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:

```bash
# 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:

```bash
npx astro add react vue
```

> [!IMPORTANT]
> Each `astro add` command modifies `astro.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:

```bash
# 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:

```bash
# 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.

```javascript
// 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.ts` for 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

```javascript
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

```javascript
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

```javascript
// 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 used `hybrid`, switch to `output: "static"` — it now covers the same use case. Individual pages can still opt into server rendering with `export const prerender = false`.

### Trailing slash

```javascript
export default defineConfig({
  trailingSlash: 'ignore',  // Accept both /about and /about/
  // trailingSlash: 'always', // Redirect to /about/
  // trailingSlash: 'never',  // Redirect to /about
});
```

> [!CAUTION]
> If `trailingSlash` is set to `always`, 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

```javascript
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

```javascript
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

```javascript
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

```javascript
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

```javascript
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

```javascript
export default defineConfig({
  prefetch: {
    prefetchAll: false,
    defaultStrategy: 'hover', // 'tap' | 'hover' | 'viewport' | 'load'
  },
});
```

### Fonts

```javascript
export default defineConfig({
  fonts: [
    {
      provider: 'google',
      name: 'Inter',
      weights: [400, 600, 700],
    },
  ],
});
```

### Scoped style strategy

```javascript
export default defineConfig({
  scopedStyleStrategy: 'attribute', // 'where' | 'class' | 'attribute'
});
```

- `attribute` — adds a `data-astro-*` attribute (default, broadest browser support)
- `class` — adds a generated class name
- `where` — 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 `---`:

```astro
---
// 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()`:

```astro
---
// 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

```astro
---
// 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:

```astro
---
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 />`:

```astro
---
// 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:

```astro
---
import Base from '../layouts/Base.astro';
---

<Base title="About Us">
  <h1>About Us</h1>
  <p>We build cool things.</p>
</Base>
```

### Named slots

```astro
---
// src/layouts/TwoColumn.astro
---

<div class="layout">
  <aside>
    <slot name="sidebar" />
  </aside>
  <main>
    <slot />
  </main>
</div>
```

```astro
<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:

```astro
<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:

```astro
<style is:global>
  /* Applies everywhere */
  body {
    margin: 0;
    font-family: system-ui;
  }
</style>
```

**Inline styles** — skip processing entirely:

```astro
<style is:inline>
  /* Injected verbatim, no bundling */
  @import url('https://fonts.googleapis.com/css2?family=Inter');
</style>
```

**CSS variables from frontmatter:**

```astro
---
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:**

```astro
---
import '../styles/global.css';
---
```

**Tailwind CSS:**

```bash
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:

```astro
<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:**

```astro
<script src="/scripts/analytics.js" is:inline></script>
```

### Markdown and MDX

Any `.md` file in `src/pages/` becomes a page:

```markdown
---
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:

```bash
npx astro add mdx
```

Then in `.mdx` files:

```mdx
---
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:

```typescript
// 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:

```astro
---
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:

```astro
---
// 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`:

```typescript
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/`:

```markdown
---
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:

```bash
npx astro sync
```

**Step 4:** List posts:

```astro
---
// 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:

```astro
---
// 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`:

```typescript
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:

```astro
---
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:

```astro
---
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:

```javascript
// astro.config.mjs
export default defineConfig({
  image: {
    remotePatterns: [
      { protocol: 'https', hostname: 'images.unsplash.com' },
      { protocol: 'https', hostname: '**.cloudinary.com' },
    ],
  },
});
```

Then use remote URLs:

```astro
<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:**

```bash
npx astro add node
```

**Set output mode:**

```javascript
// 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:**

```bash
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:

```astro
---
// 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:

```astro
---
export const prerender = false; // Works in static mode too
---
```

### Task 5: Protect routes with middleware

```typescript
// 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:**

```typescript
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

```javascript
// 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:

```text
src/pages/
├── index.astro          → /
├── about.astro          → /about
├── es/
│   ├── index.astro      → /es/
│   └── about.astro      → /es/about
└── fr/
    └── index.astro      → /fr/
```

Use i18n helpers:

```astro
---
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

```bash
npm install @astrojs/rss
```

```typescript
// 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

```bash
npx astro add sitemap
```

Set your site URL (required):

```javascript
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:

```javascript
// 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:

```typescript
// 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 import `astro:env/server` in 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:

```javascript
export default defineConfig({
  prefetch: true, // Use default strategy (hover)
  // Or configure:
  prefetch: {
    prefetchAll: false,
    defaultStrategy: 'hover', // 'tap' | 'hover' | 'viewport' | 'load'
  },
});
```

Prefetch specific links:

```astro
<!-- 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.

```astro
---
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:defer` directive 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:**

```bash
npx astro create-key
```

Add the output to your `.env`:

```text
ASTRO_KEY=<generated-key-here>
```

**Use sessions in pages:**

```astro
---
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):

```javascript
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

```bash
npx astro add mdx
```

Pass custom components to MDX content:

```astro
---
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

```bash
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

```typescript
// 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:

```astro
---
import { getCollection } from 'astro:content';
const products = await getCollection('products', ({ data }) => data.inStock);
---
```

### Task 17: Deploy to Vercel

```bash
npx astro add vercel
```

```javascript
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

```bash
# 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**

```bash
# 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:

1. Clear the Astro cache: `rm -rf node_modules/.astro`
2. Reinstall dependencies: `pnpm install --force`
3. If using a custom image service, verify its `validateOptions` export 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:

1. Stop the dev server.
2. Clear `.astro` cache: `rm -rf node_modules/.astro`
3. 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` (or `index.html`) exists.

**Dev server not reflecting file changes**

Clear cache and restart:

```bash
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:

1. Ensure your adapter is installed: `npx astro add netlify` or `npx astro add vercel`
2. Verify `output: "server"` is set when using an adapter
3. Check for missing environment variables in your deployment settings
4. 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:

```javascript
// 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:

```bash
npx astro check
```

For watch mode during development:

```bash
npx astro check --watch
```

If you see "types not found" for content collections, regenerate them:

```bash
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:

```bash
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 dev` and 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:
1. Is the component being used with a `client:only` directive? If so, the component renders client-side only — Astro scoped styles won't reach it. Use the framework's own styling.
2. Is specificity fighting you? Use `scopedStyleStrategy: 'where'` in config to reduce scoped style specificity to zero.
3. 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:

1. Use UnoCSS's `uno.css` import approach instead of `@apply`
2. Ensure your `vite` config in `astro.config.mjs` includes UnoCSS as a Vite plugin (not just a PostCSS plugin)
3. 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:

```javascript
// 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:

```typescript
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:

```typescript
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`:

```javascript
export default defineConfig({
  image: {
    remotePatterns: [
      { protocol: 'https', hostname: 'your-cdn.com' },
    ],
  },
});
```

**Sharp not found / image optimization disabled**

Install Sharp explicitly:

```bash
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:

```javascript
// 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:

```bash
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:
1. Ensure your code blocks have a language tag (` ```javascript ` not just ` ``` `)
2. Shiki supports hundreds of languages — check the Shiki language list if yours isn't highlighting
3. To switch to Prism: `npm install prism-themes` and configure in `astro.config.mjs` under `markdown.syntaxHighlight: 'prism'`

**Remark/rehype plugin not working**

```javascript
// 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:
1. The form `action` attribute uses the imported action reference, not a string path.
2. The form method is `POST`.
3. 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:

```astro
---
const result = Astro.getActionResult(actions.myAction);
---

{result?.error?.fieldErrors?.email && (
  <p class="error">{result.error.fieldErrors.email[0]}</p>
)}
```

### Session issues

**Sessions not persisting**

1. Ensure `ASTRO_KEY` is set in your environment (generated by `astro create-key`).
2. Sessions require an adapter. Check that `output: "server"` is configured.
3. 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:

```javascript
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 `.md` and `.mdx` files with frontmatter. You get a `render()` function to produce HTML.
- `type: 'data'` — for `.json` and `.yaml` files. 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()`:

```typescript
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()`:

```astro
---
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:

```javascript
// store.js
import { atom } from 'nanostores';
export const cartCount = atom(0);
```

```javascript
// 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:

```astro
---
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:

```astro
---
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:

```typescript
// 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`:

```typescript
// 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`:

```typescript
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`, output `dist`
- **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:

```dockerfile
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](https://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:

```javascript
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?**

```bash
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.*

---

_Generated May 2026 from withastro/astro at 1058428. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
