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