Desarrollo Astro con Claude Code: Content Collections, SSG y verificación
Guía práctica para construir sitios Astro con Claude Code: configuración, listados, tags, hidratación y build.
Por qué Astro encaja bien con Claude Code
Astro es una buena opción para blogs, documentación, portfolios, páginas de producto y sitios donde la mayor parte del contenido se lee más de lo que se manipula. Puedes usar componentes de React, Vue o Svelte, pero Astro no convierte todo el sitio en una aplicación cliente pesada. Su arquitectura de islas mantiene estático lo que puede ser estático y solo hidrata en el navegador las piezas interactivas, como un buscador o un filtro.
Claude Code aporta más valor cuando le pides que entienda el proyecto antes de editar. En Astro no basta con tocar una página: astro.config.mjs, src/content.config.ts, src/pages/, src/components/, imágenes, rutas y comandos de build están conectados. Si Claude Code lee primero esos archivos y explica el plan, es menos probable que añada dependencias innecesarias o reescriba más de la cuenta.
Esta guía usa el patrón actual de Astro para Content Collections: src/content.config.ts, astro/loaders y astro/zod. Antes de aplicarlo en producción, revisa las fuentes oficiales: Claude Code Quickstart, Astro Content Collections, Astro template directives y Astro routing reference. Para decisiones de arquitectura, consulta también la comparación SSR/SSG y la optimización SEO.
Empieza con un alcance concreto
Un prompt como “crea un blog Astro” es demasiado amplio. Puede provocar cambios en diseño, rutas, modelo de contenido y dependencias al mismo tiempo. Define el objetivo: sitio estático de contenido, soporte MDX, sitemap, frontmatter tipado, listado de artículos, páginas por tag y verificación de build.
npm create astro@latest my-astro-site
cd my-astro-site
npx astro add mdx sitemap tailwind
npm run dev
Después, pide a Claude Code que inspeccione antes de editar:
claude "Revisa este proyecto Astro como un sitio de contenido. Lee astro.config.mjs, src/content.config.ts, src/pages y src/components. Antes de editar, explica el plan de implementación y los comandos de verificación."
La configuración inicial puede ser pequeña:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
import tailwind from '@astrojs/tailwind';
export default defineConfig({
site: 'https://example.com',
output: 'static',
integrations: [mdx(), sitemap(), tailwind()],
markdown: {
shikiConfig: {
theme: 'github-dark',
},
},
});
Cambia site por el dominio real antes de publicar. El sitemap, las URLs canónicas y parte del SEO dependen de ese valor.
Define Content Collections con la API actual
Content Collections permite tratar los archivos Markdown y MDX como datos con forma conocida. En vez de confiar en que cada artículo tenga el mismo frontmatter, defines un schema y Astro detecta errores antes del despliegue. Esto es muy útil en sitios multilingües, donde una traducción puede romper por accidente un campo fijo.
// src/content.config.ts
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
const blog = defineCollection({
loader: glob({
base: './src/content/blog',
pattern: '**/*.{md,mdx}',
}),
schema: z.object({
title: z.string().max(80),
description: z.string().max(120),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
heroImage: z.string().optional(),
}),
});
export const collections = { blog };
Luego puedes pedir una revisión específica:
claude "Revisa src/content.config.ts para Astro 5. Comprueba API actual de Content Collections, longitud de description, exclusión de draft, updatedDate opcional y mezcla de APIs antiguas."
El beneficio práctico es claro: una fecha inválida, una descripción demasiado larga o un tags mal formado se detectan antes de que el sitio llegue a producción.
Caso 1: listado de artículos con paginación
El primer caso real es el índice del blog. No dependas del orden que devuelve getCollection(). Filtra borradores, ordena por fecha y pasa cada entrada a un componente pequeño.
---
// src/pages/blog/[...page].astro
import { getCollection } from 'astro:content';
import PostCard from '../../components/PostCard.astro';
export async function getStaticPaths({ paginate }) {
const posts = (await getCollection('blog', ({ data }) => data.draft !== true))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
return paginate(posts, { pageSize: 12 });
}
const { page } = Astro.props;
---
<section class="mx-auto max-w-5xl px-4 py-10">
<h1 class="text-3xl font-bold">Blog</h1>
<div class="mt-8 grid gap-6 md:grid-cols-2">
{page.data.map((post) => <PostCard post={post} />)}
</div>
<nav class="mt-10 flex justify-between">
{page.url.prev ? <a href={page.url.prev}>Previous</a> : <span />}
{page.url.next ? <a href={page.url.next}>Next</a> : <span />}
</nav>
</section>
El componente de tarjeta puede copiarse tal cual:
---
// src/components/PostCard.astro
import type { CollectionEntry } from 'astro:content';
type Props = {
post: CollectionEntry<'blog'>;
lang?: string;
};
const { post, lang = 'es' } = Astro.props;
const href = lang === 'ja' ? `/blog/${post.id}/` : `/${lang}/blog/${post.id}/`;
const date = post.data.updatedDate ?? post.data.pubDate;
---
<article class="rounded border border-slate-200 p-5">
<p class="text-sm text-slate-500">
<time datetime={date.toISOString()}>{date.toLocaleDateString('es-ES')}</time>
</p>
<h2 class="mt-2 text-xl font-semibold">
<a href={href}>{post.data.title}</a>
</h2>
<p class="mt-3 text-slate-700">{post.data.description}</p>
<ul class="mt-4 flex flex-wrap gap-2">
{post.data.tags.map((tag) => <li class="rounded bg-slate-100 px-2 py-1 text-xs">{tag}</li>)}
</ul>
</article>
Este patrón sirve para tres usos comunes: blog personal, base de conocimiento interna y changelog de producto. En los tres, la estructura de datos importa más que la decoración visual.
Caso 2: páginas por tag e hidratación selectiva
El segundo caso es el archivo por tag. El error típico es usar el nombre del tag directamente como URL. Los espacios, las mayúsculas y los caracteres no latinos pueden romper rutas. Separa el slug de la etiqueta visible.
---
// src/pages/tags/[tag]/[...page].astro
import { getCollection } from 'astro:content';
import PostCard from '../../../components/PostCard.astro';
const tagSlug = (tag) =>
encodeURIComponent(tag.toLowerCase().trim().replace(/\s+/g, '-'));
export async function getStaticPaths({ paginate }) {
const posts = await getCollection('blog', ({ data }) => data.draft !== true);
const groups = new Map();
for (const post of posts) {
for (const label of post.data.tags) {
const slug = tagSlug(label);
const group = groups.get(slug) ?? { slug, label, posts: [] };
group.posts.push(post);
groups.set(slug, group);
}
}
return [...groups.values()].flatMap((group) =>
paginate(group.posts, {
params: { tag: group.slug },
props: { tag: group.label },
pageSize: 10,
}),
);
}
const { page, tag } = Astro.props;
---
<section class="mx-auto max-w-5xl px-4 py-10">
<h1 class="text-3xl font-bold">Tag: {tag}</h1>
<div class="mt-8 grid gap-6 md:grid-cols-2">
{page.data.map((post) => <PostCard post={post} />)}
</div>
</section>
El tercer caso es un buscador. No conviertas toda la página en cliente solo por un componente interactivo:
---
// src/pages/index.astro
import Hero from '../components/Hero.astro';
import SearchBox from '../components/SearchBox.tsx';
import LatestPosts from '../components/LatestPosts.astro';
---
<Hero />
<SearchBox client:visible />
<LatestPosts />
client:load debe reservarse para elementos que necesitan interacción inmediata. client:visible es mejor para componentes que pueden esperar hasta entrar en pantalla.
Errores frecuentes y verificación
El primer error es copiar tutoriales antiguos de Content Collections sin mirar la versión instalada. El segundo es pedir una mejora de todo el sitio, lo que dificulta la revisión. El tercero es olvidar heroImage, description y Open Graph. El cuarto es considerar que un build correcto equivale a publicar: también hay que revisar paginación, enlaces internos, enlaces externos, móvil y CTA.
npm run build
npx astro check
npm run preview
Si falla el build, pide clasificación antes de corregir:
claude "Lee el error de npm run build. Clasifícalo como sintaxis Astro, schema de Content Collections, code fence de MDX, generación de rutas o problema de enlaces. Propón el arreglo mínimo."
CTA y resultado probado
Para empezar, la chuleta gratuita ayuda con comandos básicos. Si necesitas plantillas, revisa productos. Para equipos que quieren ordenar un sitio Astro, reglas de revisión y adopción de Claude Code, la ruta natural es formación y consultoría.
Al probar este flujo, Claude Code generó archivos .astro con rapidez, pero hizo falta revisar manualmente la API actual de Astro, la codificación de tags en URL, el uso excesivo de client:load y la ejecución real del build. El flujo más estable fue leer el proyecto, limitar el cambio, contrastar con documentación oficial, ejecutar build y revisar la vista previa.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.