Loading...
Loading...
Use when editing .astro/.mdx files, modifying astro.config.*, working with content collections (build-time or live), adding Tailwind CSS v4, using client directives (client:load/idle/visible), handling forms/actions with Zod 4, configuring server features (sessions, i18n, env vars, CSP, Cloudflare Workers), using view transitions or ClientRouter (<ClientRouter />), or setting up adapters (Node/Vercel/Netlify/Cloudflare) in an Astro project. Provides correct Astro 6 patterns, hydration guidance, view transition lifecycle, and prevents outdated Astro 3/4/5 code.
npx skill4agent add gigio1023/astro-dev-skill astro-devsearch_astro_docs()search_astro_docs({ query: "X" })references/doc-endpoints.md| What you're doing | Read this file |
|---|---|
| Project setup / core APIs / styles / scripts / middleware | |
| Content collections (schema, loader, querying, Zod 4) | |
| Blog features (RSS, pagination, tags, SEO, TOC, Shiki) | |
| Tailwind CSS (config, theming, classes, fonts) | |
| Client directives / islands / hydration | |
| Forms, actions, data mutations | |
| View transitions, ClientRouter, script lifecycle | |
| Sessions, env vars, i18n, CSP, Cloudflare, prerender | |
| Doc URLs, MCP fallback | |
loader// agents generate this (outdated)
const blog = defineCollection({ schema: z.object({...}) })
// correct pattern
import { glob } from 'astro/loaders'
const blog = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
schema: ({ image }) => z.object({...})
})image()references/content-collections.md/* agents generate this (outdated) */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* correct pattern */
@import "tailwindcss";
@theme inline {
--color-primary: oklch(0.6 0.2 250);
}@tailwindcss/vite@astrojs/tailwindreferences/tailwind.mdAstro.glob()// agents generate this (removed API)
const posts = await Astro.glob('./posts/*.md')
// correct pattern
import { getCollection } from 'astro:content'
const posts = await getCollection('blog')render()// agents generate this (outdated)
const { Content } = await post.render()
// correct pattern
import { render } from 'astro:content'
const { Content } = await render(post)astro:config:setupmarkdown.remarkPluginsexport function myIntegration(): AstroIntegration {
return {
name: 'my-plugin',
hooks: {
'astro:config:setup': ({ config, updateConfig }) => {
const existing = [...(config.markdown?.remarkPlugins || [])]
updateConfig({
markdown: { remarkPlugins: [myRemarkPlugin, ...existing] },
})
},
},
}
}integrations[]rehype-expressive-codeastro-expressive-codemarkdown.rehypePluginsclient:<!-- agents do this (wasteful) -->
<Counter client:load />
<Sidebar client:load />
<Footer client:load />
<!-- correct: choose based on urgency -->
<Counter client:load />
<Sidebar client:idle />
<Footer client:visible />client:idleclient:visibleclient:idle<!-- WRONG: user clicks before hydration, click is silently lost -->
<SearchButton client:idle />
<MobileMenu client:idle />
<!-- correct: elements users click immediately need client:load -->
<SearchButton client:load />
<MobileMenu client:load />client:loadreferences/islands-and-hydration.md// agents build this (verbose, no validation)
// src/pages/api/subscribe.ts
export const POST: APIRoute = async ({ request }) => { ... }
// correct: use Actions (typed, validated, CSRF-protected)
// src/actions/index.ts
export const server = {
subscribe: defineAction({
accept: 'form',
input: z.object({ email: z.email() }), // Zod 4: z.email(), not z.string().email()
handler: async (input) => { ... },
}),
}references/actions-and-forms.md---
// agents forget this — the page silently fails or behaves unexpectedly
export const prerender = false // REQUIRED for dynamic features
const session = Astro.cookies.get('session')
---references/server-features.mdastro:envprocess.env// agents do this (unvalidated, no type safety)
const secret = process.env.API_KEY
// correct: define schema in config, import from virtual module
import { API_KEY } from 'astro:env/server'import.meta.envastro:envprocess.envreferences/server-features.mdclass<!-- agents assume class passes through (it doesn't) -->
<Card class="mt-4" />
<!-- correct: Card.astro must accept and apply class -->
---
const { class: className, ...rest } = Astro.props
---
<div class:list={['card', className]} {...rest}>
<slot />
</div>:global()references/astro-core-patterns.md<script><!-- Script runs ONCE even if component renders 10 times -->
<script>
document.querySelectorAll('.my-btn').forEach(btn => { ... })
</script>data-*define:varsis:inlinereferences/astro-core-patterns.mdfetch()---
// In static mode, this runs ONCE at build time
const data = await fetch('https://api.example.com/data').then(r => r.json())
---export const prerender = falseclient:*// astro.config.ts
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'ko', 'ja'],
},
})redirectToDefaultLocalefalsereferences/server-features.mdastro/zodastro:content// agents generate this (deprecated in Astro 6)
import { defineCollection, z } from 'astro:content'
// correct pattern
import { defineCollection } from 'astro:content'
import { z } from 'astro/zod'astro:schemaastro/zodz.string().email()z.email(){message:}{error:}// ERRORS in Astro 6:
// - src/content/config.ts (must be src/content.config.ts)
// - defineCollection({ type: 'content' }) (type field removed)
// - defineCollection({}) without loader (loader is mandatory)
// correct: every collection needs a loader
import { defineCollection } from 'astro:content'
import { glob } from 'astro/loaders'
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
})// ERRORS in Astro 6
// astro.config.cjs — CommonJS not supported
// module.exports = { ... }
// correct: use ESM (.ts or .mjs)
// astro.config.ts
import { defineConfig } from 'astro/config'
export default defineConfig({ ... })<ClientRouter />astro:page-load// breaks on first load or after navigation
initFeature()
document.addEventListener('astro:after-swap', initFeature)
// correct: covers both initial load AND navigations
document.addEventListener('astro:page-load', initFeature)<ClientRouter />// listeners lost when DOM is swapped during navigation
btn.addEventListener('click', handler)
// correct: survives DOM swaps
document.addEventListener('click', (e) => {
if ((e.target as HTMLElement).closest('.btn')) handler()
})astro:before-swapafter-swapdocument.addEventListener('astro:before-swap', (e) => {
e.newDocument.documentElement.setAttribute('data-theme',
localStorage.getItem('theme') || 'light')
})after-swapdisplay: none<style is:global>global.cssreferences/view-transitions.mdtemplates/// astro.config.ts
import { defineConfig, fontProviders } from 'astro/config'
import tailwindcss from '@tailwindcss/vite'
import mdx from '@astrojs/mdx'
import react from '@astrojs/react'
import sitemap from '@astrojs/sitemap'
export default defineConfig({
site: 'https://example.com',
integrations: [mdx(), react(), sitemap()],
vite: {
plugins: [tailwindcss()],
},
fonts: [
{
provider: fontProviders.google(),
name: 'Inter',
cssVariable: '--font-inter',
weights: ['100 900'],
},
],
})package.json"astro".ts.mjs.cjssrc/content.config.tssrc/content/config.ts@tailwindcss/vite@astrojs/tailwind