# Astro — Claude Code rules
You are working in an Astro project (islands architecture, content-driven sites).
The core principle: **render to HTML & CSS and ship zero client-side JavaScript by
default. Add JS only where interactivity is genuinely needed.**
## Components (.astro)
- A `.astro` file has a component script in a code fence (`---`) and a template below it.
- The component script runs at **build time / on the server only**. It is stripped from
the page sent to the browser — put imports, data fetching, and API calls here.
- The template is HTML with `{ }` expressions referencing script variables.
- Props come in via `Astro.props`. Define an `interface Props` for type safety, e.g.
`const { title } = Astro.props;`. Support defaults in the destructure when useful.
- Use `` for children; named slots via `` + `slot="x"` on the child.
Provide fallback content inside the slot when no children are passed.
- Compose pages from small components. Astro components render with **zero client JS** by default.
## Islands & hydration (client: directives)
- An island is an interactive UI component on an otherwise static page. Islands run in
isolation and hydrate independently, in parallel — they don't block each other.
- Only components marked with a `client:*` directive ship JavaScript. Pick the lightest one:
- `client:load` — hydrate immediately on page load (high-priority, above-the-fold UI).
- `client:idle` — hydrate once the browser is idle (lower-priority UI).
- `client:visible` — hydrate only when the component scrolls into view (default for most things).
- `client:media={QUERY}` — hydrate when a CSS media query matches.
- `client:only="react"` — skip server render; render on the client only (name the framework).
- **No directive = static HTML, no JS.** Don't add `client:*` to anything that isn't interactive.
- For dynamic server-rendered content, use a **server island** via the `server:defer` directive
instead of shipping client JS.
- Plain `.astro` components **cannot** take `client:*` — only framework components hydrate.
## UI framework components (React / Vue / Svelte / Preact / Solid)
- Without a directive, framework components also render to static HTML — no JS shipped.
- Only Astro components can mix multiple frameworks on one page. Framework components
**cannot import `.astro` files**.
- Props passed to a hydrated island must be serialisable (objects, numbers, strings, Arrays,
Map, Set, Date, RegExp, URL, BigInt, typed arrays). **Functions cannot be passed.**
- Children: React/Preact/Solid use a `children` prop; Svelte/Vue use ``.
## Content (content collections)
- Manage structured content (blog posts, docs, etc.) with **content collections**, stored
outside `src/pages/`.
- Define collections in `src/content.config.ts` with `defineCollection()`, exported as a
single `collections` object.
- Use a loader: `glob()` for a directory of Markdown/MDX/JSON/YAML/TOML, `file()` for one
file with many entries. Set the `base` path explicitly.
- Give each collection a **Zod `schema`** for validated, type-safe frontmatter, e.g.
`z.object({ title: z.string(), pubDate: z.coerce.date() })`. Schemas are optional but recommended.
- Query with `getCollection('blog')` and `getEntry('dogs', 'poodle')` from `astro:content`.
- Render entries with the standalone `render()`: `const { Content } = await render(entry);`.
- Link entries across collections with `reference()`.
## Don't
- Don't add `client:*` to non-interactive components, or reach for a framework when plain
Astro + a small `