Skip to main content

astro-developer

Expert guidance for building content-driven websites with Astro, covering island architecture, content collections, SSR/SSG, integrations, and performance optimization. Use when the user asks about astro developer, astro developer best practices, or needs guidance on astro developer implementation. Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.

Datos de origen

Repositorio
FerroxLabs/murage
Última actividad en el origen
7 de octubre de 2026 a las 18:05
Idioma detectado de SKILL.md
inglés
Estrellas
10
Forks
2

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
2 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
astro-developer
description
Expert guidance for building content-driven websites with Astro, covering island architecture, content collections, SSR/SSG, integrations, and performance optimization. Use when the user asks about astro developer, astro developer best practices, or needs guidance on astro developer implementation. Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.
license
Apache-2.0
metadata
{"author":"Ferrox Labs","version":"1.0.0","tags":"web-development frontend javascript","category":"web-development","subcategory":"frontend-frameworks","depends":"","disclaimer":"none","difficulty":"intermediate"}
# Astro Developer You are an expert Astro framework developer specializing in content-driven websites, island architecture, and multi-framework integration. You guide developers through building fast, SEO-optimized sites using Astro's content-first approach, partial hydration model, and extensive integration ecosystem. You prioritize zero-JS-by-default while enabling rich interactivity where needed. ## Project Setup and Configuration ### Starter Configuration ```typescript // astro.config.mjs import { defineConfig } from 'astro/config'; import mdx from '@astrojs/mdx'; import sitemap from '@astrojs/sitemap'; import react from '@astrojs/react'; import tailwind from '@astrojs/tailwind'; import vercel from '@astrojs/vercel'; export default defineConfig({ site: '[reference URL]', output: 'static', // 'static' | 'server' | 'hybrid' integrations: [ mdx(), sitemap(), react(), tailwind({ applyBaseStyles: false }) ], adapter: vercel(), // only needed for server/hybrid image: { domains: ['cdn.example.com'], service: { entrypoint: 'astro/assets/services/sharp' } }, vite: { ssr: { noExternal: ['some-package'] } } }); ``` ### Project Structure ``` src/ components/ Header.astro # Astro components (zero JS) Footer.astro PostCard.astro SearchWidget.tsx # React island (interactive) ThemeToggle.svelte # Svelte island (interactive) content/ config.ts # Content collection schemas blog/ first-post.md second-post.mdx docs/ getting-started.md api-reference.md authors/ jane-doe.json layouts/ BaseLayout.astro BlogLayout.astro DocsLayout.astro pages/ index.astro about.astro blog/ index.astro [slug].astro [...page].astro # Paginated listing api/ search.ts # API endpoint rss.xml.ts # RSS feed styles/ global.css public/ fonts/ og-images/ ``` ## Content Collections ### Schema Definition ```typescript // src/content/config.ts import { defineCollection, z, reference } from 'astro:content'; const blog = defineCollection({ type: 'content', // Markdown/MDX files schema: ({ image }) => z.object({ title: z.string().max(80), description: z.string().max(160), pubDate: z.coerce.date(), updatedDate: z.coerce.date().optional(), author: reference('authors'), cover: image().refine(img => img.width >= 800, { message: 'Cover image must be at least 800px wide' }), tags: z.array(z.string()).default([]), draft: z.boolean().default(false), category: z.enum(['tutorial', 'opinion', 'release', 'case-study']) }) }); const authors = defineCollection({ type: 'data', // JSON/YAML files schema: z.object({ name: z.string(), bio: z.string(), avatar: z.string().url(), social: z.object({ github: z.string().optional(), twitter: z.string().optional() }).optional() }) }); const docs = defineCollection({ type: 'content', schema: z.object({ title: z.string(), order: z.number(), section: z.string(), updated: z.coerce.date().optional() }) }); export const collections = { blog, authors, docs }; ``` ### Querying Content ```astro --- // src/pages/blog/index.astro import { getCollection, getEntry } from 'astro:content'; import BaseLayout from '../../layouts/BaseLayout.astro'; import PostCard from '../../components/PostCard.astro'; const posts = (await getCollection('blog', ({ data }) => !data.draft)) .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); --- <BaseLayout title="Blog"> <h1>Blog</h1> <section class="post-grid"> {posts.map(post => <PostCard {post} />)} </section> </BaseLayout> ``` ### Dynamic Routes from Content ```astro --- // src/pages/blog/[slug].astro import { getCollection, type CollectionEntry } from 'astro:content'; import BlogLayout from '../../layouts/BlogLayout.astro'; export async function getStaticPaths() { const posts = await getCollection('blog', ({ data }) => !data.draft); return posts.map(post => ({ params: { slug: post.slug }, props: { post } })); } type Props = { post: CollectionEntry<'blog'> }; const { post } = Astro.props; const { Content, headings, remarkPluginFrontmatter } = await post.render(); const author = await getEntry(post.data.author); --- <BlogLayout title={post.data.title} description={post.data.description}> <article> <h1>{post.data.title}</h1> <p class="meta">By {author.data.name} | {post.data.pubDate.toLocaleDateString()}</p> <p class="reading-time">{remarkPluginFrontmatter.readingTime}</p> <Content /> </article> </BlogLayout> ``` ## Island Architecture ### Client Directives ```astro --- import StaticNav from '../components/Nav.astro'; // Zero JS import SearchWidget from '../components/SearchWidget.tsx'; // React import ThemeToggle from '../components/ThemeToggle.svelte'; // Svelte import Analytics from '../components/Analytics.tsx'; import Comments from '../components/Comments.tsx'; import HeavyChart from '../components/Chart.tsx'; --- <!-- No directive: renders HTML at build time, zero JS shipped --> <StaticNav /> <!-- client:load - hydrate immediately on page load --> <ThemeToggle client:load /> <!-- client:idle - hydrate when browser is idle --> <SearchWidget client:idle /> <!-- client:visible - hydrate when element enters viewport --> <Comments client:visible /> <!-- client:visible with rootMargin - start loading earlier --> <HeavyChart client:visible={{ rootMargin: '200px' }} /> <!-- client:media - hydrate when media query matches --> <MobileMenu client:media="(max-width: 768px)" /> <!-- client:only="react" - skip SSR, client render only --> <Analytics client:only="react" /> ``` ### Passing Data to Framework Islands ```astro --- import ProductGallery from '../components/ProductGallery.tsx'; const product = await get('/api/products/123').then(r => r.json()); --- <!-- Serializable props are passed to the island --> <ProductGallery client:visible images={product.images} name={product.name} initialSlide={0} /> <!-- Slots work for static content inside islands --> <ProductGallery client:visible images={product.images}> <p>This static HTML renders server-side inside the island slot.</p> </ProductGallery> ``` ### Sharing State Between Islands ```typescript // src/stores/cartStore.ts (using nanostores - framework agnostic) import { atom, map, computed } from 'nanostores'; export type CartItem = { id: string; name: string; price: number; qty: number }; export const $cartItems = map<Record<string, CartItem>>({}); export const $cartTotal = computed($cartItems, items => Object.values(items).reduce((sum, i) => sum + i.price * i.qty, 0) ); export function addToCart(item: Omit<CartItem, 'qty'>) { const existing = $cartItems.get()[item.id]; $cartItems.setKey(item.id, { ...item, qty: existing ? existing.qty + 1 : 1 }); } export function removeFromCart(id: string) { const current = { ...$cartItems.get() }; delete current[id]; $cartItems.set(current); } ``` ```tsx // React island consuming the store import { useStore } from '@nanostores/react'; import { $cartItems, $cartTotal, removeFromCart } from '../stores/cartStore'; export function CartDrawer() { const items = useStore($cartItems); const total = useStore($cartTotal); return ( <aside> {Object.values(items).map(item => ( <div key={item.id}> {item.name} x{item.qty} - ${item.price * item.qty} <button onClick={() => removeFromCart(item.id)}>Remove</button> </div> ))} <strong>Total: ${total}</strong> </aside> ); } ``` ## API Endpoints and SSR ### API Route ```typescript // src/pages/api/search.ts import type { APIRoute } from 'astro'; export const GET: APIRoute = async ({ url }) => { const query = url.searchParams.get('q')?.trim(); if (!query || query.length < 2) { return new Response(JSON.stringify({ error: 'Query too short' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } const results = await searchIndex(query); // your search logic return new Response(JSON.stringify({ results }), { headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=300' } }); }; ``` ### Server-Side Rendering (Hybrid Mode) ```astro --- // src/pages/dashboard.astro // This page opts into SSR in hybrid mode export const prerender = false; const session = Astro.cookies.get('session')?.value; if (!session) return Astro.redirect('/login', 302); const user = await validateSession(session); const stats = await fetchDashboardStats(user.id); --- <BaseLayout title="Dashboard"> <h1>Welcome, {user.name}</h1> <DashboardWidgets client:load stats={stats} userId={user.id} /> </BaseLayout> ``` ## Image Optimization Use the built-in `<Image>` component from `astro:assets` with `widths`, `sizes`, `format` (avif/webp), and `quality` props for automatic responsive image optimization. Import local images for build-time processing; remote images require domains to be listed in `astro.config.mjs` under `image.domains`. ## Performance and SEO Checklist - [ ] Set `output: 'static'` unless you specifically need SSR - [ ] Use content collections with Zod schemas for type safety - [ ] Apply `client:visible` or `client:idle` instead of `client:load` where possible - [ ] Use `nanostores` for shared state between framework islands - [ ] Optimize images with the built-in `<Image>` component and `avif`/`webp` formats - [ ] Add `<ViewTransitions />` from `astro:transitions` for SPA-like navigation - [ ] Generate RSS with `@astrojs/rss` and sitemap with `@astrojs/sitemap` - [ ] Set `compressHTML: true` in astro config for production - [ ] Use `prefetch` on key navigation links for instant transitions - [ ] Add Open Graph meta tags in layouts for social sharing - [ ] Configure `Cache-Control` headers for API endpoints - [ ] Keep islands small and focused; avoid hydrating entire page sections ## Decision Matrix: Rendering Strategy | Content Type | Strategy | Config | |---|---|---| | Blog posts, docs, marketing | Static (SSG) | `output: 'static'` (default) |
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub