---
name: astro-6-server-islands
description: "Astro 6 content sites — server islands, Content Layer API, partial hydration, Cloudflare deploy. Production Astro 6: server islands (server:defer + slot fallbacks) for dynamic content in static pages, Content Layer API with glob/file loaders and custom remote loaders, type-safe collections via defineCollection + Zod schemas, MDX rendering, client:* hydration directives (load/idle/visible/media), View Transitions, and SSR deploy with @astrojs/cloudflare adapter. Covers Astro 6 migration from Content Collections and astro:env."
license: MIT
---

# Astro 6 Islands

A skill for building production content sites with Astro 6 — server islands for dynamic regions inside static pages, the Content Layer API for type-safe collections, partial hydration directives, and Cloudflare deployment. It's aimed at developers shipping blogs, docs, and marketing sites who want mostly-static HTML with selectively dynamic and interactive bits, without turning the whole page into an SPA.

## When to use it

- A page is statically generated, but one region (cart count, personalized greeting, live stock) must be per-request. Reach for a server island instead of making the whole route SSR.
- You're pulling content from Markdown/MDX on disk, or from a CMS/API, and want validated, typed entries.
- You have a React/Vue/Svelte widget that should ship zero JS until it's actually needed.
- You're deploying to Cloudflare Workers/Pages and want the adapter wired correctly.

## How it works

**Server islands** use the `server:defer` directive on a component. Astro renders the static shell immediately and streams the island's HTML in afterward from the server. Provide a placeholder with the named `fallback` slot:

```astro
<Avatar server:defer>
  <div slot="fallback">Loading…</div>
</Avatar>
```

**Content Layer API** defines collections in `src/content.config.ts` with `defineCollection`, a `loader`, and a Zod `schema`. Built-in `glob()` and `file()` loaders cover local content; you can write a custom loader for remote sources. Query with `getCollection()` / `getEntry()`, and `render()` to get the `<Content />` component (MDX supported via `@astrojs/mdx`).

```ts
// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({ title: z.string(), date: z.coerce.date(), draft: z.boolean().default(false) }),
});
export const collections = { blog };
```

**Client directives** control hydration of framework components: `client:load`, `client:idle`, `client:visible`, and `client:media="(max-width: 50em)"`.

```bash
curl -fsSL --create-dirs -o ~/.claude/skills/astro-6-server-islands/SKILL.md https://claudeskil.com/skills/astro-6-server-islands/SKILL.md
npm create astro@latest
npx astro add cloudflare
```

## Tips & gotchas

- Server islands require an adapter and an SSR-capable host; on a fully static build with no adapter they won't defer.
- Island props are encrypted and sent to the server, but don't pass secrets through them — treat the boundary like any request input.
- The config file is `src/content.config.ts` in Astro 6 (the old `src/content/config.ts` location is legacy). Run `astro sync` to regenerate `astro:content` types after schema edits.
- `client:only="react"` skips SSR entirely — use it only for components that can't render on the server.

## FAQ

**Server island vs. `client:*` directive?** Server islands defer *server-rendered* HTML (no client JS, good for personalization). Client directives hydrate *interactive* components in the browser.

**Can I cache a server island?** Yes — set response/Cache-Control headers in the island; each island is its own request and can be cached independently of the shell.

**Do I need a database for the Content Layer?** No. `glob`/`file` loaders read local files; a custom loader handles remote APIs.
