Content Collections en Astro 6: la Content Layer API explicada
La nueva Content Layer API de Astro 5/6 cambia cómo se definen y consultan las colecciones de contenido. Te explico las diferencias con la API legacy y cómo migrar.
Si conoces las Content Collections de Astro 4, la nueva Content Layer API te resultará familiar pero con diferencias importantes. Si eres nuevo en Astro, estás de suerte: empiezas con la API moderna.
¿Qué es la Content Layer API?
Las Content Collections son la forma que tiene Astro de gestionar contenido estructurado (posts de blog, proyectos, autores…) con validación de tipos en tiempo de build.
La Content Layer API es la nueva versión de este sistema. El cambio principal es que ahora las colecciones usan loaders — funciones que saben cómo cargar el contenido.
API legacy vs Content Layer API
Antes (Astro 4, API legacy)
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content', // 'content' o 'data'
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
}),
});
export const collections = { blog };
Ahora (Astro 5/6, Content Layer API)
// src/content.config.ts ← cambió la ubicación del archivo
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(),
pubDate: z.coerce.date(),
}),
});
export const collections = { blog };
Las diferencias son:
- El archivo cambió de lugar:
src/content/config.ts→src/content.config.ts typefue reemplazado porloader: usasglob()para archivos locales- Mayor flexibilidad: puedes crear loaders personalizados para APIs, bases de datos, etc.
Cómo consultar colecciones
Esto no cambió demasiado:
import { getCollection } from 'astro:content';
// Todos los posts, filtrando borradores
const posts = await getCollection('blog', ({ data }) => !data.draft);
// Ordenados por fecha descendente
posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
Cómo renderizar el contenido
Aquí sí hubo un cambio en Astro 5/6:
// API legacy (Astro 4)
const { Content } = await entry.render();
// Content Layer API (Astro 5/6)
import { render } from 'astro:content';
const { Content } = await render(entry);
El campo id en lugar de slug
Con el loader glob, las entradas tienen un campo id que deriva del nombre del archivo. Para mi-primer-post.md, el id es "mi-primer-post". En la API legacy esto se llamaba slug.
Así quedan las rutas dinámicas:
---
// src/pages/blog/[slug].astro
import { getCollection, render } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((entry) => ({
params: { slug: entry.id }, // usamos entry.id
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await render(entry);
---
<Content />
¿Por qué loaders?
La abstracción del loader abre posibilidades muy interesantes. Con la API legacy solo podías cargar archivos locales. Con loaders puedes:
- Cargar contenido de una API REST o GraphQL
- Leer desde una base de datos
- Integrar con cualquier CMS headless
- Combinar varias fuentes en una sola colección
Para la mayoría de proyectos personales, el loader glob() es todo lo que necesitas. Pero si en el futuro quieres mover tu contenido a un CMS, solo cambias el loader — el resto del código queda igual.
La Content Layer API es un paso en la dirección correcta. Más flexible, mejor tipada y preparada para casos de uso más complejos.