Les Content Collections sont l’un des guide général d’Astro depuis la version 2, et la version 5 sortie fin 2024 les a profondément refondues avec le nouveau « Content Layer ». Plus rapides, plus flexibles, type-safe par défaut, et capables de charger du contenu depuis Markdown local mais aussi depuis Notion, Strapi, Sanity ou n’importe quelle source externe via des loaders custom. Voici le guide complet pour exploiter Content Collections en 2026.
Voir notre guide complet Astro 5 pour les bases.
Pourquoi Content Collections
- Type-safety : chaque collection a un schéma Zod. Frontmatter validé au build. Erreur immédiate si un article a un champ manquant ou de mauvais type.
- API uniforme :
getCollection,getEntry,renderfonctionnent identiquement quelle que soit la source - Performance : Astro 5 indexe les contenus pour des requêtes rapides au build et au runtime
- Multi-source : Markdown local + JSON + APIs externes coexistent dans la même collection
- Auto-complétion dans VS Code grâce à TypeScript généré automatiquement
Étape 1 — Définir une collection
// src/content/config.ts
import { defineCollection, z } from "astro:content";
import { glob, file } from "astro/loaders";
const blog = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "src/content/blog" }),
schema: ({ image }) => z.object({
title: z.string().min(10).max(100),
description: z.string().min(50).max(160),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
author: z.string().default("ITSkillsCenter"),
tags: z.array(z.string()).default([]),
cover: image().optional(),
draft: z.boolean().default(false),
}),
});
const formations = defineCollection({
loader: file("src/content/formations.json"),
schema: z.object({
id: z.string(),
title: z.string(),
duration: z.string(),
price: z.number(),
level: z.enum(["debutant", "intermediaire", "avance"]),
}),
});
export const collections = { blog, formations };
Étape 2 — Écrire du contenu
---
# src/content/blog/coolify-dakar.md
title: "Déployer Coolify depuis Dakar — guide pratique"
description: "Comment installer et configurer Coolify sur un VPS Hetzner depuis le Sénégal."
pubDate: 2026-04-27
author: "ITSkillsCenter"
tags: ["coolify", "vps", "afrique", "devops"]
cover: "../../assets/coolify-dakar.jpg"
---
Si vous gérez une PME au Sénégal, Coolify change la donne...
Étape 3 — Lister tous les articles
---
// src/pages/blog/index.astro
import { getCollection } from "astro:content";
import Layout from "../../layouts/Base.astro";
const allPosts = await getCollection("blog", ({ data }) => !data.draft);
const sorted = allPosts.sort(
(a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime()
);
---
<Layout title="Blog">
<ul class="post-list">
{sorted.map((post) => (
<li>
<a href={`/blog/${post.id}/`}>
<h2>{post.data.title}</h2>
<time datetime={post.data.pubDate.toISOString()}>
{post.data.pubDate.toLocaleDateString("fr-SN")}
</time>
<p>{post.data.description}</p>
</a>
</li>
))}
</ul>
</Layout>
Étape 4 — Page d’article dynamique
---
// src/pages/blog/[...slug].astro
import { getCollection, render } from "astro:content";
import Layout from "../../layouts/Base.astro";
export async function getStaticPaths() {
const posts = await getCollection("blog", ({ data }) => !data.draft);
return posts.map((post) => ({
params: { slug: post.id },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<Layout title={post.data.title} description={post.data.description}>
<article>
<h1>{post.data.title}</h1>
<time>{post.data.pubDate.toLocaleDateString("fr-SN")}</time>
<Content />
</article>
</Layout>
Étape 5 — Loader custom (Notion, API)
// src/loaders/notion-loader.ts
import type { Loader } from "astro/loaders";
import { Client } from "@notionhq/client";
export function notionLoader(databaseId: string): Loader {
return {
name: "notion-loader",
load: async ({ store, parseData }) => {
const notion = new Client({ auth: process.env.NOTION_TOKEN });
const response = await notion.databases.query({ database_id: databaseId });
for (const page of response.results) {
const data = await parseData({
id: page.id,
data: { title: extractTitle(page), updated: page.last_edited_time },
});
store.set({ id: page.id, data });
}
},
};
}
Étape 6 — Filtres et recherche
// Tous les articles avec tag "afrique"
const africaPosts = await getCollection("blog", ({ data }) =>
data.tags.includes("afrique")
);
// Articles par auteur
const byAuthor = await getCollection("blog", ({ data }) =>
data.author === "ITSkillsCenter"
);
// Article spécifique
import { getEntry } from "astro:content";
const entry = await getEntry("blog", "coolify-dakar");
Étape 7 — RSS et sitemap
// src/pages/rss.xml.ts
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
export async function GET(context) {
const posts = await getCollection("blog", ({ data }) => !data.draft);
return rss({
title: "ITSkillsCenter Blog",
description: "Formation IT pour l'Afrique de l'Ouest",
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.pubDate,
description: post.data.description,
link: `/blog/${post.id}/`,
})),
});
}
Adaptation Afrique de l’Ouest
Pour un blog tech francophone qui cible l’Afrique de l’Ouest (comme itskillscenter.io), Content Collections + i18n natif Astro 5 permet de publier facilement en français, anglais et arabe, avec routing automatique et SEO multi-langue. Idéal pour toucher Sénégal, CI, Maroc et zone MENA.
Erreurs fréquentes
| Erreur | Cause | Solution |
|---|---|---|
| « Cannot find module ‘astro:content' » | npm dev pas lancé | Astro génère les types au build/dev |
| Frontmatter validation failed | Champ requis manquant | Lire l’erreur, ajouter le champ dans le .md |
| Image non trouvée | Path relatif au .md, pas au site | ../../assets/file.jpg depuis content/blog/ |
| Build lent avec 1000+ articles | Loader sync sur grosse source | Async loader avec cache, ou pagination |
Pour aller plus loin
- Guide complet Astro 5
- Server Islands Astro
- Déployer Astro
- Documentation Content Collections : docs.astro.build/en/guides/content-collections