← Tous les articles

Ajouter un blog en MDX dans Next.js 16

Le tutoriel que j'ai suivi pour ce site : des articles en fichiers, sans base de données, avec Turbopack et la coloration du code.

Lithor

· 2 min de lecture

Sommaire
  1. Installer les paquets
  2. Configurer Next.js
  3. Créer le fichier de composants
  4. Charger un article depuis une route
  5. Lister les articles
  6. Ce que j'en retiens

Envie d'un blog sans base de données ni service externe ? Avec MDX, un article est un fichier de votre dépôt. Voici les étapes que j'ai suivies pour ce site, avec les pièges rencontrés.

Installer les paquets

Le guide officiel demande quatre paquets. J'ajoute deux plugins pour les identifiants de titres et la coloration du code.

Terminal
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
npm install rehype-slug rehype-pretty-code shiki

Configurer Next.js

Le fichier de configuration déclare les extensions de pages et branche MDX. Avec Turbopack, les plugins se donnent par leur nom (une chaîne), car des fonctions JavaScript ne peuvent pas être passées au code Rust.

next.config.ts
import createMDX from "@next/mdx";
import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  pageExtensions: ["ts", "tsx", "md", "mdx"],
};
 
const withMDX = createMDX({
  options: {
    rehypePlugins: [
      "rehype-slug",
      ["rehype-pretty-code", { theme: { light: "github-light", dark: "github-dark-dimmed" } }],
    ],
  },
});
 
export default withMDX(nextConfig);

Créer le fichier de composants

Avec l'App Router, mdx-components.tsx est obligatoire. C'est aussi l'endroit pour remplacer un élément HTML par votre composant.

src/mdx-components.tsx
import type { MDXComponents } from "mdx/types";
 
export function useMDXComponents(): MDXComponents {
  return {};
}

Charger un article depuis une route

Une route dynamique importe le fichier dont le nom correspond au paramètre. generateStaticParams produit les pages au build, et dynamicParams = false renvoie une 404 pour tout le reste.

src/app/blog/[slug]/page.tsx
export const dynamicParams = false;
 
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const { default: Post } = await import(`@/content/blog/${slug}.mdx`);
 
  return <Post />;
}

Notez que params est une promesse : il faut l'attendre avant de lire slug.

Lister les articles

Pour la liste, je lis le dossier avec fs, puis j'importe chaque fichier pour récupérer son export meta. Le temps de lecture se calcule à partir du nombre de mots du fichier brut.

Ce que j'en retiens

  • Les articles sont versionnés avec le code, chaque correction laisse une trace.
  • Aucun service tiers : le blog fonctionne hors ligne en développement.
  • Ajouter un article demande un nouveau build, ce qui convient à un site personnel.

Sources et pour aller plus loin