65 lines | 3.9 KB

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 <slot /> for children; named slots via <slot name="x" /> + 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 <slot />.

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 <script> will do.
  • Don't pass functions (or non-serialisable values) as props to a hydrated island.
  • Don't import a .astro component from inside a framework component.
  • Don't put content collection files under src/pages/, and don't skip the Zod schema.
  • Don't expect client:only components to render on the server — they won't.