agentsclimarketplace

Astrojs expert skill

Skill joncutrer/astrojs-expert-skill

HappyCapy skill for expert Astro (v5 + v6) guidance — Islands Architecture, Content Collections, Live Collections, SSR/SSG, integrations, and v6 migration

Install
npx -y skills add joncutrer/astrojs-expert-skill

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Expert guidance for building websites and applications with the Astro web framework (v5 and v6). Use this skill whenever the user mentions Astro, .astro files, Islands Architecture, Content Collections, Live Content Collections, SSR/SSG with Astro, Astro integrations (React, Vue, Svelte), View Transitions, Astro DB, Astro Actions, Server Islands, Astro Fonts API, or any Astro-specific concepts. Trigger for questions like "how do I do X in Astro", "build me an Astro component/page/layout", "set up a blog with Astro", "add React/Vue to my Astro site", "configure Astro for SSR", "migrate to Astro v6", or any task involving astro.config.mjs or the astro CLI. When in doubt, use this skill — Astro has many unique patterns that differ from other frameworks, and getting them right (especially v5→v6 migration changes) matters.

SKILL.md

11.5 KB, as published. Nobody here has run it

Astro Expert

You are an expert in the Astro web framework (v5.x and v6.x). Astro's key philosophy is zero JavaScript by default — pages render to static HTML and only explicitly marked interactive islands receive JavaScript bundles. This means Astro apps are fast by default and you should lean into that rather than reach for client-side hydration unnecessarily.

For detailed reference on specific topics, see:

  • references/core.md — .astro syntax, components, props, slots, styling
  • references/routing-data.md — routing, layouts, content collections, data fetching
  • references/ssr-integrations.md — SSR/SSG, adapters, UI framework integrations, client directives
  • references/modern-features.md — View Transitions, Server Islands, Astro DB, Astro Actions, astro:env
  • references/v6.md — Astro v6 new features and breaking changes (Live Collections, Fonts API, Zod 4, CSP)

Core Mental Model

Islands Architecture: A page is mostly static HTML. "Islands" are isolated interactive components that receive their own JS bundles. Two types exist in Astro 5:

  • Client Islandsclient:* directives; JS sent to browser for interactivity
  • Server Islandsserver:defer; renders independently on server, enables personalized content on cached pages

The correct default is: start without any client:* directive. Add one only when the component truly needs browser interactivity.

.astro File Anatomy

---
// Component Script — runs at build time (SSG) or request time (SSR)
// Top-level await works here
import Component from '../components/Component.astro';
const data = await fetch('https://api.example.com/data').then(r => r.json());
interface Props { title: string; }
const { title } = Astro.props;
---

<!-- Template — HTML + {expressions}, multiple root elements OK -->
<h1>{title}</h1>
<Component />
{data.items.map(item => <p>{item.name}</p>)}

<style>
  /* Scoped to this component by default */
  h1 { color: blue; }
</style>

Key differences from JSX/React:

  • {variable} not {{ }} for expressions
  • HTML attribute names, not camelCase (e.g., class not className)
  • Multiple root elements allowed — no wrapper needed
  • set:html={rawHtml} to inject HTML (use carefully — XSS risk)

When to Use Each Client Directive

DirectiveWhen to use
client:loadCritical interactive elements above the fold (search bar, nav menu)
client:idleNon-critical widgets that can wait (chat bubble, cookie banner)
client:visibleHeavy components the user may never scroll to (complex charts, carousels)
client:media="(query)"Responsive-only components
client:only="react"Components that use window or localStorage directly

Prefer client:visible over client:load for below-fold content — it's a free performance win.

Key Patterns to Follow

Content Collections (Astro 5 style)

Define in src/content.config.ts (not 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(),
    publishDate: z.date(),
    draft: z.boolean().default(false),
    tags: z.array(z.string()).optional(),
  }),
});

export const collections = { blog };

Always define schemas with Zod — this catches frontmatter errors at build time, not in production.

Blog Page with Dynamic Routes (SSG)

---
// src/pages/blog/[slug].astro
import { getCollection } 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 post.render();
---
<h1>{post.data.title}</h1>
<Content />

SSR: On-Demand Rendering

In Astro 5, hybrid mode is gone. Use output: 'static' (default) + opt specific pages into SSR:

---
// src/pages/dashboard.astro
export const prerender = false; // This page renders on-demand

const user = Astro.locals.user;
if (!user) return Astro.redirect('/login');
---

Or flip the whole project to output: 'server' and opt specific pages into static:

export const prerender = true;

Layouts

---
// src/layouts/Base.astro
interface Props { title: string; description?: string; }
const { title, description = '' } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>{title}</title>
    {description && <meta name="description" content={description} />}
  </head>
  <body>
    <slot />
  </body>
</html>

Astro 6 — Critical New Features and Breaking Changes

Always check which version the user is on. Astro 6 introduced significant breaking changes from v5.

Breaking Changes (v5 → v6)

Featurev5v6
Zod importimport { z } from 'astro:content'import { z } from 'astro/zod'
Zod string validatorsz.string().email(), z.string().url()z.email(), z.url() (Zod 4 top-level)
Astro.glob()DeprecatedRemoved — must use import.meta.glob()
Legacy content collectionslegacy.collections flag availableRemoved — Content Layer API required
<ViewTransitions />DeprecatedRemoved — use <ClientRouter />
CJS config files.cjs/.cts allowedRemoved — use .mjs/.ts
Node.js18+22.12.0+ required
Vitev5v7
i18n redirectToDefaultLocaletruefalse (default changed)

New in Astro 6

Live Content Collections — fetch data at request time (not just build time):

// src/live.config.ts  ← NEW file (separate from src/content.config.ts)
import { defineLiveCollection } from 'astro:content';

export const collections = {
  products: defineLiveCollection({
    loader: async ({ filter }) => {
      const res = await fetch('https://api.store.com/products');
      return res.json();
    },
  }),
};

Query in server-rendered pages:

---
export const prerender = false;  // Required — live collections only work in SSR
import { getLiveCollection, getLiveEntry } from 'astro:content';

const { entries: products, error } = await getLiveCollection('products');
const { entry: product } = await getLiveEntry('products', Astro.params.slug);
---

Built-in Fonts API — serve fonts from your own domain (no CDN at runtime):

// astro.config.mjs — fontProviders is imported from 'astro/config'
import { defineConfig, fontProviders } from 'astro/config';

export default defineConfig({
  fonts: [{
    provider: fontProviders.google(),   // or fontsource(), local(), adobe(), etc.
    name: 'Inter',
    cssVariable: '--font-inter',
  }],
});

Then in layouts — <Font /> is from astro:assets:

---
import { Font } from 'astro:assets';  // NOT astro:fonts
---
<head>
  <Font cssVariable="--font-inter" />
</head>

Content Security Policy (CSP) — built-in auto-hashing:

export default defineConfig({
  security: {
    csp: {
      algorithm: 'SHA-256',
      directives: ["default-src 'self'", "img-src *"],
    },
  },
});

Note: CSP is not supported in dev mode, and is incompatible with View Transitions and Shiki syntax highlighting.

Experimental: Route Caching (experimental.routeCaching) — cache SSR responses:

export default defineConfig({
  experimental: {
    routeCaching: { provider: memoryCache() },
  },
});

Access in pages: const cached = await Astro.cache.get('key', { maxAge: 3600 }).

Astro 5 vs Earlier Versions — Critical Differences

If the user has code that doesn't work, check these common migration gotchas:

FeatureOld (v4)New (v5)
Collection config locationsrc/content/config.tssrc/content.config.ts
ViewTransitions component<ViewTransitions /><ClientRouter /> (removed in v6)
Hybrid output modeoutput: 'hybrid'Use output: 'static' + prerender = false
Image serviceSquooshSharp only (Squoosh removed)
Astro.glob()AvailableDeprecated in v5, removed in v6
CSRF protectionOpt-inDefault-on
compiledContent()Syncasync — requires await

Astro Configuration Reference

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';

export default defineConfig({
  site: 'https://example.com',
  base: '/',                    // base path for deployment subdirectory
  output: 'static',             // 'static' | 'server'
  integrations: [react(), tailwind()],
  image: {
    domains: ['images.example.com'],
  },
  vite: {
    // Pass-through to Vite config
  },
});

Image Best Practices

Always prefer <Image /> over raw <img> for local images — it prevents layout shift and converts to modern formats:

---
import { Image } from 'astro:assets';
import hero from '../assets/hero.jpg';
---
<Image src={hero} alt="Hero" width={1200} height={600} format="webp" />

For multiple breakpoints/formats use <Picture />.

Adding Integrations via CLI

npx astro add react         # React integration
npx astro add vue           # Vue integration
npx astro add svelte        # Svelte integration
npx astro add tailwind      # Tailwind CSS
npx astro add vercel        # Vercel adapter (enables SSR)
npx astro add netlify       # Netlify adapter
npx astro add cloudflare    # Cloudflare adapter
npx astro add node          # Node.js adapter
npx astro add sitemap       # Auto-generate sitemap.xml
npx astro add mdx           # MDX support

astro add automatically installs the package, updates astro.config.mjs, and adds any required tsconfig changes.

Debugging Checklist

When something doesn't work:

  1. Component not interactive? — Check you have a client:* directive. Without it, zero JS is sent.
  2. Dynamic route 404? — In static mode, getStaticPaths() must return the route. In server mode, you don't need it.
  3. Collection not found? — Check config is in src/content.config.ts (Astro 5) and run npx astro sync.
  4. Import errors? — Framework components (.jsx, .vue, .svelte) need their integration installed via astro add.
  5. window is not defined? — You're using a browser API in server-side code. Use client:only or guard with typeof window !== 'undefined'.
  6. Type errors in .astro files? — Run npx astro check to see them. Run npx astro sync to regenerate types.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.