MDX

Markdown est un langage de balisage léger utilisé pour formater du texte. Il vous permet d'écrire en utilisant une syntaxe en texte brut et de le convertir en HTML structurellement valide. Il est couramment utilisé pour écrire du contenu sur des sites web et des blogs.

Vous écrivez...

I **love** using [Next.js](https://nextjs.org/)

Sortie :

<p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>

MDX est un sur-ensemble de markdown qui vous permet d'écrire du JSX directement dans vos fichiers markdown. C'est une manière puissante d'ajouter de l'interactivité dynamique et d'intégrer des composants React dans votre contenu.

Next.js peut prendre en charge à la fois le contenu MDX local dans votre application et les fichiers MDX distants récupérés dynamiquement sur le serveur. Le plugin Next.js gère la transformation du markdown et des composants React en HTML, y compris la prise en charge de l'utilisation dans les composants serveur (par défaut dans App Router).

@next/mdx

Le package @next/mdx est utilisé pour configurer Next.js afin qu'il puisse traiter le markdown et MDX. Il récupère les données à partir de fichiers locaux, vous permettant de créer des pages avec une extension .mdx, directement dans vos répertoires /pages ou /app.

Voyons comment configurer et utiliser MDX avec Next.js.

Premiers pas

Installez les packages nécessaires pour afficher MDX :

Terminal
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

Mettez à jour le fichier next.config.js à la racine de votre projet pour le configurer pour utiliser MDX :

next.config.js
const withMDX = require('@next/mdx')()

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Configurez `pageExtensions` pour inclure les fichiers MDX
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // Optionnellement, ajoutez toute autre configuration Next.js ci-dessous
}

module.exports = withMDX(nextConfig)

Ensuite, créez une nouvelle page MDX dans le répertoire /pages :

  votre-projet
  ├── pages
  │   └── ma-page-mdx.mdx
  └── package.json

Maintenant, vous pouvez utiliser le markdown et importer des composants React directement dans votre page MDX :

import { MonComposant } from 'mes-composants'

# Bienvenue sur ma page MDX !

Ceci est du texte en **gras** et _italique_.

Voici une liste en markdown :

- Un
- Deux
- Trois

Découvrez mon composant React :

<MonComposant />

Naviguer vers la route /ma-page-mdx devrait afficher votre MDX rendu.

MDX distant

Si vos fichiers ou contenu markdown/MDX se trouvent ailleurs, vous pouvez les récupérer dynamiquement sur le serveur. Ceci est utile pour le contenu stocké dans un dossier local séparé, un CMS, une base de données ou tout autre endroit.

Il existe deux packages communautaires populaires pour récupérer du contenu MDX :

Bon à savoir : Veuillez procéder avec prudence. MDX est compilé en JavaScript et exécuté sur le serveur. Vous ne devez récupérer du contenu MDX qu'à partir d'une source de confiance, sinon cela peut conduire à une exécution de code à distance (RCE).

L'exemple suivant utilise next-mdx-remote :

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'

interface Props {
  mdxSource: MDXRemoteSerializeResult
}

export default function PageMdxDistante({ mdxSource }: Props) {
  return <MDXRemote {...mdxSource} />
}

export async function getStaticProps() {
  // Texte MDX - peut provenir d'un fichier local, d'une base de données, d'un CMS, d'une requête fetch, etc.
  const res = await fetch('https:...')
  const mdxText = await res.text()
  const mdxSource = await serialize(mdxText)
  return { props: { source: mdxSource } }
}
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

export default function PageMdxDistante({ mdxSource }) {
  return <MDXRemote {...mdxSource} />
}

export async function getStaticProps() {
  // Texte MDX - peut provenir d'un fichier local, d'une base de données, d'un CMS, d'une requête fetch, etc.
  const res = await fetch('https:...')
  const mdxText = await res.text()
  const mdxSource = await serialize(mdxText)
  return { props: { source: mdxSource } }
}

Naviguer vers la route /ma-page-mdx-distante devrait afficher votre MDX rendu.

Mises en page

Pour partager une mise en page autour des pages MDX, créez un composant de mise en page :

export default function MiseEnPageMdx({ children }: { children: React.ReactNode }) {
  // Créez ici toute mise en page ou style partagé
  return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MiseEnPageMdx({ children }) {
  // Créez ici toute mise en page ou style partagé
  return <div style={{ color: 'blue' }}>{children}</div>
}

Ensuite, importez le composant de mise en page dans la page MDX, encapsulez le contenu MDX dans la mise en page et exportez-le :

import MiseEnPageMdx from '../components/mise-en-page-mdx'

# Bienvenue sur ma page MDX !

export default function PageMDX({ children }) {
  return <MiseEnPageMdx>{children}</MiseEnPageMdx>;

}

Plugins Remark et Rehype

Vous pouvez éventuellement fournir des plugins remark et rehype pour transformer le contenu MDX.

Par exemple, vous pouvez utiliser remark-gfm pour prendre en charge GitHub Flavored Markdown.

Comme l'écosystème remark et rehype est uniquement ESM, vous devrez utiliser next.config.mjs comme fichier de configuration.

next.config.mjs
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Configurez `pageExtensions` pour inclure les fichiers MDX
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // Optionnellement, ajoutez toute autre configuration Next.js ci-dessous
}

const withMDX = createMDX({
  // Ajoutez ici les plugins markdown souhaités
  options: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [],
  },
})

// Fusionnez la configuration MDX avec la configuration Next.js
export default withMDX(nextConfig)

Frontmatter

Le frontmatter est une paire clé/valeur de type YAML qui peut être utilisée pour stocker des données sur une page. @next/mdx ne prend pas en charge le frontmatter par défaut, bien qu'il existe de nombreuses solutions pour ajouter du frontmatter à votre contenu MDX, telles que :

Pour accéder aux métadonnées de la page avec @next/mdx, vous pouvez exporter un objet meta depuis le fichier .mdx :

export const meta = {
  author: 'John Doe',
}

# Ma page MDX

Éléments personnalisés

L'un des aspects agréables de l'utilisation du markdown est qu'il correspond aux éléments HTML natifs, rendant l'écriture rapide et intuitive :

Voici une liste en markdown :

- Un
- Deux
- Trois

Ce qui génère le HTML suivant :

<p>Voici une liste en markdown :</p>

<ul>
  <li>Un</li>
  <li>Deux</li>
  <li>Trois</li>
</ul>

Lorsque vous souhaitez styliser vos propres éléments pour une apparence personnalisée de votre site web ou application, vous pouvez utiliser des shortcodes. Ce sont vos propres composants personnalisés qui correspondent aux éléments HTML.

Pour ce faire, créez un fichier mdx-components.tsx à la racine de votre application (le dossier parent de pages/ ou src/) et ajoutez des éléments personnalisés :

import type { MDXComponents } from 'mdx/types'
import Image from 'next/image'

// Ce fichier vous permet de fournir des composants React personnalisés
// à utiliser dans les fichiers MDX. Vous pouvez importer et utiliser n'importe quel
// composant React que vous souhaitez, y compris des styles en ligne,
// des composants d'autres bibliothèques, et plus encore.

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    // Permet de personnaliser les composants intégrés, par exemple pour ajouter du style.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...props}
      />
    ),
    ...components,
  }
}
import Image from 'next/image'

// Ce fichier vous permet de fournir des composants React personnalisés
// à utiliser dans les fichiers MDX. Vous pouvez importer et utiliser n'importe quel
// composant React que vous souhaitez, y compris des styles en ligne,
// des composants d'autres bibliothèques, et plus encore.

export function useMDXComponents(components) {
  return {
    // Permet de personnaliser les composants intégrés, par exemple pour ajouter du style.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...props}
      />
    ),
    ...components,
  }
}

Approfondissement : Comment transformer le markdown en HTML ?

React ne comprend pas nativement le markdown. Le texte brut markdown doit d'abord être transformé en HTML. Cela peut être accompli avec remark et rehype.

remark est un écosystème d'outils autour du markdown. rehype est le même, mais pour HTML. Par exemple, l'extrait de code suivant transforme le markdown en HTML :

import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'

main()

async function main() {
  const file = await unified()
    .use(remarkParse) // Convertit en AST markdown
    .use(remarkRehype) // Transforme en AST HTML
    .use(rehypeSanitize) // Nettoie l'entrée HTML
    .use(rehypeStringify) // Convertit l'AST en HTML sérialisé
    .process('Hello, Next.js!')

  console.log(String(file)) // <p>Hello, Next.js!</p>
}

L'écosystème remark et rehype contient des plugins pour la coloration syntaxique, les liens vers les titres, la génération d'une table des matières, et plus encore.

Lorsque vous utilisez @next/mdx comme montré ci-dessus, vous n'avez pas besoin d'utiliser remark ou rehype directement, car cela est géré pour vous. Nous le décrivons ici pour une meilleure compréhension de ce que fait le package @next/mdx en arrière-plan.

Utilisation du compilateur MDX basé sur Rust (Expérimental)

Next.js prend en charge un nouveau compilateur MDX écrit en Rust. Ce compilateur est encore expérimental et n'est pas recommandé pour une utilisation en production. Pour utiliser le nouveau compilateur, vous devez configurer next.config.js lorsque vous le passez à withMDX :

next.config.js
module.exports = withMDX({
  experimental: {
    mdxRs: true,
  },
})

Liens utiles