ITSkillsCenter
Développement Web

Content Collections Astro 5 : tutoriel complet 2026

5 min de lecture

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, render fonctionnent 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

ErreurCauseSolution
« Cannot find module ‘astro:content' »npm dev pas lancéAstro génère les types au build/dev
Frontmatter validation failedChamp requis manquantLire l’erreur, ajouter le champ dans le .md
Image non trouvéePath relatif au .md, pas au site../../assets/file.jpg depuis content/blog/
Build lent avec 1000+ articlesLoader sync sur grosse sourceAsync loader avec cache, ou pagination

Pour aller plus loin

Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité