Communitygithub.com

Patrones de Astro (0xDarkMatter/claude-mods)

Patrones del framework Astro: arquitectura de islas, colecciones de contenido, estrategias de renderizado y despliegue. Útil para: astro, arquitectura de islas, colecciones de contenido, astro cloudflare, transiciones de vista, hidratación parcial, astrojs, SSG, SSR, renderizado híbrido, adaptador de astro.

¿Qué es claude-mods)?

Este skill proporciona una guía completa y práctica para el desarrollo con el framework Astro, cubriendo sus patrones fundamentales como la arquitectura de islas, colecciones de contenido, y estrategias de renderizado (SSG, SSR, e híbrido). Incluye árboles de decisión detallados para elegir la estrategia de renderizado y el destino de despliegue adecuados (Cloudflare Pages, Vercel, Netlify o Node.js), así como tablas de referencia rápida para directivas de hidratación, transiciones de vista y un checklist de seguridad para producción. El skill es útil para desarrolladores que buscan optimizar el rendimiento de sus sitios Astro mediante la hidratación parcial de componentes interactivos (React, Vue, Svelte) y la gestión eficiente de contenido con esquemas tipados en Zod. También documenta errores comunes y sus soluciones, lo que lo convierte en un recurso valioso tanto para equipos nuevos como experimentados que trabajan con Astro.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/0xDarkMatter/claude-mods/tree/HEAD/skills/astro-ops

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Operaciones de Astro

Hechos verificados al 2026-07.

Patrones completos para el desarrollo con el framework Astro: arquitectura de islas, colecciones de contenido, estrategias de renderizado, transiciones de vista y despliegue multiplataforma.

Árbol de Decisión de Estrategia de Renderizado

¿Qué estrategia de renderizado?
│
├─ ¿El contenido es mayormente estático (blog, docs, marketing)?
│  ├─ SÍ → ¿Cambia menos de una vez al día?
│  │  ├─ SÍ → SSG (output: 'static')
│  │  │        TTFB más rápido, almacenable en CDN, costo de ejecución cero
│  │  └─ NO  → Híbrido (output: 'hybrid')
│  │           Estático por defecto + SSR opt-in por ruta
│  └─ NO  → ¿Necesita personalización cada página?
│     ├─ SÍ → SSR (output: 'server')
│     │        Dinámico por solicitud, consciente de autenticación, datos en tiempo real
│     └─ NO  → Híbrido (output: 'hybrid')
│              Caparazón estático + islas de servidor para partes dinámicas
│
├─ ¿La aplicación necesita interactividad en tiempo real (dashboard, SPA)?
│  ├─ SÍ → ¿Es un SPA completo con enrutamiento del lado del cliente?
│  │  ├─ SÍ → Considera React/Vue SPA en su lugar, o Astro + client:only
│  │  └─ NO  → Híbrido + arquitectura de islas
│  │           Islas interactivas en páginas estáticas
│  └─ NO  → SSG (output: 'static')
│
├─ ¿Preocupa el tiempo de construcción (>10k páginas)?
│  ├─ SÍ → Híbrido con renderizado bajo demanda
│  │        Prerrenderizar páginas populares, SSR para la cola larga
│  └─ NO  → SSG lo maneja bien
│
└─ ¿Necesitas computación en el borde (baja latencia global)?
   ├─ SÍ → SSR + adaptador Cloudflare/Vercel Edge
   └─ NO  → SSR + adaptador Node o SSG

Configuración

// astro.config.mjs
import { defineConfig } from 'astro/config';

// SSG (por defecto) - todas las páginas prerrenderizadas en tiempo de construcción
export default defineConfig({
  output: 'static',
});

// SSR - todas las páginas renderizadas bajo solicitud
export default defineConfig({
  output: 'server',
  adapter: cloudflare(), // o vercel(), netlify(), node()
});

// Híbrido - estático por defecto, SSR opt-in por página
export default defineConfig({
  output: 'hybrid',
  adapter: cloudflare(),
});
---
// En modo híbrido, opta POR NO prerrenderizar páginas específicas:
export const prerender = false;
// En modo SSR, opta POR prerrenderizar:
export const prerender = true;
---

Referencia Rápida de Arquitectura de Islas

DirectivaHidrata CuandoJS EnviadoCaso de Uso
client:loadInmediatamente al cargar la páginaBundle completoInteractividad sobre el pliegue (nav, CTA hero)
client:idleCuando la página está inactiva (requestIdleCallback)Bundle completoInteractividad bajo el pliegue (formulario de comentarios, chat)
client:visibleCuando se desplaza al viewportBundle completoComponentes al final de la página (widget de pie de página, carrusel)
client:mediaCuando la media query coincideBundle completoNav solo para móviles, componentes responsive
client:only="react"Inmediatamente, omite SSR por completoBundle completoComponentes que no pueden usar SSR (canvas, WebGL)
(ninguna)Nunca - solo HTML estáticoCero JSContenido estático, tarjetas, encabezados
---
import NavBar from '../components/NavBar.tsx';
import CommentForm from '../components/CommentForm.tsx';
import ImageCarousel from '../components/ImageCarousel.svelte';
import MobileMenu from '../components/MobileMenu.vue';
import ThreeScene from '../components/ThreeScene.tsx';
---

<!-- Se carga inmediatamente - interactividad crítica -->
<NavBar client:load />

<!-- Se carga cuando la página está inactiva - no crítico -->
<CommentForm client:idle />

<!-- Se carga al desplazarse al viewport - perezoso -->
<ImageCarousel client:visible />

<!-- Se carga solo en móvil -->
<MobileMenu client:media="(max-width: 768px)" />

<!-- Solo cliente, sin SSR (WebGL no puede ejecutarse en el servidor) -->
<ThreeScene client:only="react" />

Inicio Rápido de Colecciones de Contenido

Definir Esquema

// src/content.config.ts (Astro 5) o src/content/config.ts (Astro 4)
import { defineCollection, z, reference } 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(),
    description: z.string().max(160),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    heroImage: z.string().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    author: reference('authors'), // Referencia a otra colección
  }),
});

const authors = defineCollection({
  loader: glob({ pattern: '**/*.json', base: './src/content/authors' }),
  schema: z.object({
    name: z.string(),
    avatar: z.string(),
    bio: z.string(),
    socials: z.object({
      twitter: z.string().optional(),
      github: z.string().optional(),
    }).optional(),
  }),
});

export const collections = { blog, authors };

Consultar Colecciones

---
import { getCollection, getEntry } from 'astro:content';

// Obtener todas las publicaciones de blog no borrador, ordenadas por fecha
const posts = (await getCollection('blog', ({ data }) => !data.draft))
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

// Obtener una sola entrada
const post = await getEntry('blog', 'my-first-post');

// Resolver una referencia
const author = await getEntry(post.data.author);

// Renderizar contenido
const { Content, headings } = await post.render();
---

<Content />

Colecciones de Contenido vs CMS Externo

CriterioColecciones de ContenidoCMS Externo (Payload, etc.)
Tipo de contenidoMarkdown/MDX local, docs, blogsModelos de datos relacionales
AutoresDesarrolladores (control de versiones)Editores (UI de administración, autenticación multiusuario)
ValidaciónTipos seguros vía Zod en tiempo de construcciónEsquemas del CMS + contratos API
Cadencia de actualizaciónSe despliega con el sitioIndependiente de los despliegues
Necesidades de APINinguna (consultas en tiempo de construcción)REST/GraphQL para otros consumidores
Flujo de trabajoPRs de Git, revisión simpleFlujos editoriales, borradores, roles

Regla general: comienza con Colecciones de Contenido; recurre a un CMS solo cuando desarrolladores no técnicos necesiten publicar sin un despliegue, o cuando el contenido sea genuinamente relacional.

Referencia de Estructura del Proyecto

project-root/
├── astro.config.mjs          # Configuración de Astro
├── tsconfig.json              # Configuración de TypeScript (extiende astro/tsconfigs)
├── package.json
├── public/                    # Activos estáticos (copiados tal cual)
│   ├── favicon.svg
│   ├── robots.txt
│   └── og-image.png
├── src/
│   ├── pages/                 # Enrutamiento basado en archivos
│   │   ├── index.astro        # → /
│   │   ├── about.astro        # → /about
│   │   ├── blog/
│   │   │   ├── index.astro    # → /blog
│   │   │   └── [slug].astro   # → /blog/:slug (dinámico)
│   │   ├── api/
│   │   │   └── search.ts      # → /api/search (endpoint API)
│   │   └── [...slug].astro    # → catch-all/404
│   ├── layouts/
│   │   ├── BaseLayout.astro   # Caparazón HTML, <head>, estilos globales
│   │   └── BlogPost.astro     # Diseño de publicación de blog
│   ├── components/
│   │   ├── Header.astro       # Componente Astro estático
│   │   ├── Footer.astro
│   │   ├── NavBar.tsx         # Isla React
│   │   └── Counter.svelte     # Isla Svelte
│   ├── content/               # Archivos fuente de colecciones de contenido
│   │   ├── blog/
│   │   │   ├── post-one.md
│   │   │   └── post-two.mdx
│   │   └── authors/
│   │       └── jane.json
│   ├── content.config.ts      # Esquemas de colecciones (Astro 5)
│   ├── middleware.ts           # Middleware de solicitud/respuesta
│   ├── styles/
│   │   └── global.css
│   └── lib/                   # Utilidades compartidas
│       ├── utils.ts
│       └── constants.ts
└── .env                       # Variables de entorno

Referencia Rápida de Transiciones de Vista

---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---

<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

Directivas de Transición

<!-- Persistir elemento entre páginas (mantiene estado, evita re-renderizado) -->
<audio transition:persist id="player">
  <source src="/music.mp3" />
</audio>

<!-- Transición nombrada para emparejamiento de animación -->
<img transition:name="hero" src={post.heroImage} />

<!-- Animación personalizada -->
<div transition:animate="slide">Contenido</div>
<div transition:animate="fade">Contenido</div>
<div transition:animate="none">Sin animación</div>

<!-- Persistir con nombre (para múltiples elementos persistentes) -->
<video transition:persist="media-player" />

Eventos del Ciclo de Vida

<script>
  document.addEventListener('astro:before-preparation', (e) => {
    // Antes de obtener la nueva página - cancelar navegación, mostrar carga
  });

  document.addEventListener('astro:after-preparation', (e) => {
    // Nueva página obtenida, antes del intercambio
  });

  document.addEventListener('astro:before-swap', (e) => {
    // Personalizar el comportamiento del intercambio DOM
  });

  document.addEventListener('astro:after-swap', () => {
    // DOM actualizado - reinicializar scripts
  });

  document.addEventListener('astro:page-load', () => {
    // Página completamente cargada (se dispara en la carga inicial y en cada navegación)
    // Úsalo en lugar de DOMContentLoaded con View Transitions
  });
</script>

Manejo de Atrás/Adelante

// astro.config.mjs
export default defineConfig({
  prefetch: {
    prefetchAll: true,         // Precargar todos los enlaces al pasar el ratón
    defaultStrategy: 'hover',  // 'hover' | 'tap' | 'viewport' | 'load'
  },
});
<!-- Control de precarga por enlace -->
<a href="/about" data-astro-prefetch>Precargar al pasar el ratón (por defecto)</a>
<a href="/blog" data-astro-prefetch="viewport">Precargar cuando sea visible</a>
<a href="/contact" data-astro-prefetch="load">Precargar inmediatamente</a>
<a href="/external" data-astro-prefetch="false">Sin precarga</a>

Árbol de Decisión de Despliegue

¿Dónde desplegar?
│
├─ ¿Necesitas computación en el borde + ecosistema Cloudflare (KV, D1, R2)?
│  └─ Cloudflare Pages/Workers
│     Adaptador: @astrojs/cloudflare
│     Mejor para: Borde global, enlaces Workers, rentabilidad
│
├─ ¿Necesitas serverless + ecosistema Vercel (ISR, analíticas)?
│  └─ Vercel
│     Adaptador: @astrojs/vercel
│     Mejor para: Migración desde Next.js, optimización de imágenes, ISR
│
├─ ¿Necesitas serverless + ecosistema Netlify (formularios, identidad)?
│  └─ Netlify
│     Adaptador: @astrojs/netlify
│     Mejor para: JAMstack, formularios integrados, pruebas divididas
│
├─ ¿Necesitas control total del servidor (Docker, runtime personalizado)?
│  └─ Node.js (independiente o Express/Fastify)
│     Adaptador: @astrojs/node
│     Mejor para: Autogestionado, WebSocket, procesos de larga duración
│
└─ ¿Sitio puramente estático (sin necesidad de SSR)?
   └─ Cualquier host estático (GitHub Pages, S3, Cloudflare Pages)
      Sin adaptador necesario, output: 'static'
      Mejor para: Blogs, docs, sitios de marketing

Instalación de Adaptador

# Cloudflare
npx astro add cloudflare

# Vercel
npx astro add vercel

# Netlify
npx astro add netlify

# Node.js
npx astro add node

Errores Comunes

ErrorPor quéSolución
Errores de discrepancia de hidrataciónEl HTML del servidor difiere del renderizado del cliente (fechas, IDs aleatorios, APIs del navegador)Usa client:only para componentes dependientes del navegador, o asegura un renderizado determinista
import.meta.env indefinido en el clienteSolo las variables con prefijo PUBLIC_ están expuestas al código del lado del clienteRenombra a PUBLIC_MY_VAR o pásalas vía props desde el servidor
Rutas dinámicas 404 en SSGgetStaticPaths() no devuelve todos los parámetros posiblesAsegúrate de que getStaticPaths() devuelva cada ruta válida, o cambia a híbrido/SSR
Imágenes no optimizadasUsar <img> en lugar del componente <Image /> de AstroImporta desde astro:assets: import { Image } from 'astro:assets' y usa importaciones locales para src
SSR falla sin adaptadoroutput: 'server' o 'hybrid' requiere un adaptador de despliegueInstala el adaptador: npx astro add cloudflare (o vercel, netlify, node)
Componentes MDX no se renderizanLos componentes personalizados no se pasan al contenido MDXPasa componentes vía <Content components={{ MyComponent }} /> o usa la configuración MDX en astro.config.mjs
Los cambios en el esquema de la colección de contenido no se reflejanLa generación de tipos está en caché, tipos .astro obsoletosEjecuta astro sync para regenerar los tipos, reinicia el servidor de desarrollo
client:* en componentes AstroLas directivas de cliente solo funcionan en componentes de framework (React, Vue, Svelte)Los componentes Astro son solo estáticos; extrae las partes interactivas a un componente del framework
document / window no está definidoEl código del lado del servidor no puede acceder a los globales del navegadorProtege con if (typeof window !== 'undefined') o muévelo a client:only
Los estilos se filtran entre componentesUsar CSS global en lugar de estilos con ámbitoUsa <style> (con ámbito por defecto en .astro) o <style is:global> intencionalmente
Los scripts se rompen con View TransitionsDOMContentLoaded solo se dispara una vez con View TransitionsUsa el evento astro:page-load en su lugar, que se dispara en cada navegación
Variables de entorno faltan en producción.env no cargado o variables de entorno de la plataforma no configuradasUsa envField en astro.config.mjs para validación; configura las variables en el panel de la plataforma

Lista de Verificación de Seguridad en Producción

Para cada despliegue de producción, aborda:

  • Encabezados CSP - configura un Content-Security-Policy restrictivo (ver patrones de middleware en references/deployment.md)
  • Restricciones de imágenes remotas - aplica listas de permitidos explícitas image.domains / remotePatterns; nunca derives URLs de imágenes de la entrada del usuario (riesgo SSRF)
  • Validación del encabezado Host - verifica que el host de la solicitud coincida con los dominios esperados en el middleware (solo SSR/híbrido)
  • Gestión de secretos - en Cloudflare, usa Workers Bindings (wrangler secret put), no variables de entorno fijadas en el código; en otros lugares, usa almacenes de secretos de la plataforma
  • Solo HTTPS - asegúrate de que todos los recursos externos (scripts, imágenes, fuentes) se carguen a través de HTTPS
  • Validación de entrada - sanitiza toda la entrada del usuario en contextos SSR (parámetros de consulta, cuerpos de formularios, cookies)

Archivos de Referencia

ArchivoContenidosLíneas
references/content-collections.mdPatrones de esquema, tipos Zod, consultas, MDX, API de capa de contenido, migraciones~500
references/islands-rendering ... [truncado para traducción]

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills relacionados