Comment utiliser Markdown et MDX dans Next.js
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 la 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 leur utilisation dans les composants serveur (par défaut dans App Router).
Bon à savoir : Consultez le Portfolio Starter Kit pour un exemple complet fonctionnel.
Installer les dépendances
Le package @next/mdx
, ainsi que les packages associés, sont utilisés pour configurer Next.js afin qu'il puisse traiter Markdown et MDX. Il récupère les données à partir de fichiers locaux, vous permettant de créer des pages avec une extension .md
ou .mdx
, directement dans vos répertoires /pages
ou /app
.
Installez ces packages pour afficher MDX avec Next.js :
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
Configurer next.config.mjs
Mettez à jour le fichier next.config.mjs
à la racine de votre projet pour le configurer afin d'utiliser MDX :
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Configurez `pageExtensions` pour inclure les fichiers Markdown et MDX
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
// Optionnellement, ajoutez toute autre configuration Next.js ci-dessous
}
const withMDX = createMDX({
// Ajoutez ici les plugins Markdown souhaités
})
// Fusionnez la configuration MDX avec la configuration Next.js
export default withMDX(nextConfig)
Cela permet aux fichiers .mdx
d'agir comme des pages, des routes ou des imports dans votre application.
Gestion des fichiers .md
Par défaut, next/mdx
ne compile que les fichiers avec l'extension .mdx
. Pour gérer les fichiers .md
avec webpack, mettez à jour l'option extension
:
const withMDX = createMDX({
extension: /\.(md|mdx)$/,
})
Bon à savoir : Turbopack ne prend actuellement pas en charge l'option
extension
et ne supporte donc pas les fichiers.md
.
Ajouter un fichier mdx-components.tsx
Créez un fichier mdx-components.tsx
(ou .js
) à la racine de votre projet pour définir les composants MDX globaux. Par exemple, au même niveau que pages
ou app
, ou dans src
si applicable.
import type { MDXComponents } from 'mdx/types'
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
}
}
export function useMDXComponents(components) {
return {
...components,
}
}
Bon à savoir :
mdx-components.tsx
est requis pour utiliser@next/mdx
avec App Router et ne fonctionnera pas sans lui.- En savoir plus sur la convention de fichier
mdx-components.tsx
.- Apprenez comment utiliser des styles et composants personnalisés.
Afficher MDX
Vous pouvez afficher MDX en utilisant le routage basé sur les fichiers de Next.js ou en important des fichiers MDX dans d'autres pages.
Utilisation du routage basé sur les fichiers
Lorsque vous utilisez le routage basé sur les fichiers, vous pouvez utiliser des pages MDX comme n'importe quelle autre page.
Dans les applications App Router, cela inclut la possibilité d'utiliser les métadonnées.
Créez une nouvelle page MDX dans le répertoire /app
:
my-project
├── app
│ └── mdx-page
│ └── page.(mdx/md)
|── mdx-components.(tsx/js)
└── package.json
Vous pouvez utiliser MDX dans ces fichiers, et même importer des composants React directement dans votre page MDX :
import { MyComponent } from 'my-component'
# Bienvenue sur ma page MDX !
Voici du texte en **gras** et en _italique_.
Voici une liste en Markdown :
- Un
- Deux
- Trois
Découvrez mon composant React :
<MyComponent />
Naviguer vers la route /mdx-page
devrait afficher votre page MDX rendue.
Utilisation des imports
Créez une nouvelle page dans le répertoire /app
et un fichier MDX où vous le souhaitez :
.
├── app/
│ └── mdx-page/
│ └── page.(tsx/js)
├── markdown/
│ └── welcome.(mdx/md)
├── mdx-components.(tsx/js)
└── package.json
Vous pouvez utiliser MDX dans ces fichiers, et même importer des composants React directement dans votre page MDX :
import { MyComponent } from 'my-component'
# Bienvenue sur ma page MDX !
Voici du texte en **gras** et en _italique_.
Voici une liste en Markdown :
- Un
- Deux
- Trois
Découvrez mon composant React :
<MyComponent />
Importez le fichier MDX dans la page pour afficher le contenu :
import Welcome from '@/markdown/welcome.mdx'
export default function Page() {
return <Welcome />
}
import Welcome from '@/markdown/welcome.mdx'
export default function Page() {
return <Welcome />
}
Naviguer vers la route /mdx-page
devrait afficher votre page MDX rendue.
Utilisation des imports dynamiques
Vous pouvez importer des composants MDX dynamiques au lieu d'utiliser le routage par système de fichiers pour les fichiers MDX.
Par exemple, vous pouvez avoir un segment de route dynamique qui charge des composants MDX à partir d'un répertoire séparé :

generateStaticParams
peut être utilisé pour prérendre les routes fournies. En marquant dynamicParams
comme false
, l'accès à une route non définie dans generateStaticParams
renverra une erreur 404.
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const { default: Post } = await import(`@/content/${slug}.mdx`)
return <Post />
}
export function generateStaticParams() {
return [{ slug: 'welcome' }, { slug: 'about' }]
}
export const dynamicParams = false
export default async function Page({ params }) {
const { slug } = await params
const { default: Post } = await import(`@/content/${slug}.mdx`)
return <Post />
}
export function generateStaticParams() {
return [{ slug: 'welcome' }, { slug: 'about' }]
}
export const dynamicParams = false
Bon à savoir : Assurez-vous de spécifier l'extension de fichier
.mdx
dans votre import. Bien qu'il ne soit pas nécessaire d'utiliser des alias de chemin de module (par exemple,@/content
), cela simplifie votre chemin d'import.
Utiliser des styles et composants personnalisés
Markdown, lorsqu'il est rendu, correspond à des éléments HTML natifs. Par exemple, écrire le Markdown suivant :
## Ceci est un titre
Voici une liste en Markdown :
- Un
- Deux
- Trois
Génère le HTML suivant :
<h2>Ceci est un titre</h2>
<p>Voici une liste en Markdown :</p>
<ul>
<li>Un</li>
<li>Deux</li>
<li>Trois</li>
</ul>
Pour styliser votre Markdown, vous pouvez fournir des composants personnalisés qui correspondent aux éléments HTML générés. Les styles et composants peuvent être implémentés globalement, localement et avec des mises en page partagées.
Styles et composants globaux
Ajouter des styles et composants dans mdx-components.tsx
affectera tous les fichiers MDX de votre application.
import type { MDXComponents } from 'mdx/types'
import Image, { ImageProps } 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={{ color: 'red', fontSize: '48px' }}>{children}</h1>
),
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...(props as ImageProps)}
/>
),
...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={{ color: 'red', fontSize: '48px' }}>{children}</h1>
),
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...props}
/>
),
...components,
}
}
Styles et composants locaux
Vous pouvez appliquer des styles et composants locaux à des pages spécifiques en les passant dans les composants MDX importés. Ceux-ci fusionneront avec et remplaceront les styles et composants globaux.
import Welcome from '@/markdown/welcome.mdx'
function CustomH1({ children }) {
return <h1 style={{ color: 'blue', fontSize: '100px' }}>{children}</h1>
}
const overrideComponents = {
h1: CustomH1,
}
export default function Page() {
return <Welcome components={overrideComponents} />
}
import Welcome from '@/markdown/welcome.mdx'
function CustomH1({ children }) {
return <h1 style={{ color: 'blue', fontSize: '100px' }}>{children}</h1>
}
const overrideComponents = {
h1: CustomH1,
}
export default function Page() {
return <Welcome components={overrideComponents} />
}
Mises en page partagées
Pour partager une mise en page entre les pages MDX, vous pouvez utiliser la prise en charge intégrée des mises en page avec App Router.
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Créez ici toute mise en page ou style partagé
return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MdxLayout({ children }) {
// Créez ici toute mise en page ou style partagé
return <div style={{ color: 'blue' }}>{children}</div>
}
Utilisation du plugin Tailwind Typography
Si vous utilisez Tailwind pour styliser votre application, l'utilisation du plugin @tailwindcss/typography
vous permettra de réutiliser votre configuration et vos styles Tailwind dans vos fichiers markdown.
Le plugin ajoute un ensemble de classes prose
qui peuvent être utilisées pour ajouter des styles typographiques aux blocs de contenu provenant de sources comme le markdown.
Installez Tailwind Typography et utilisez-le avec les layouts partagés pour ajouter la classe prose
souhaitée.
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Créez ici tout layout ou style partagé
return (
<div className="prose prose-headings:mt-8 prose-headings:font-semibold prose-headings:text-black prose-h1:text-5xl prose-h2:text-4xl prose-h3:text-3xl prose-h4:text-2xl prose-h5:text-xl prose-h6:text-lg dark:prose-headings:text-white">
{children}
</div>
)
}
export default function MdxLayout({ children }) {
// Créez ici tout layout ou style partagé
return (
<div className="prose prose-headings:mt-8 prose-headings:font-semibold prose-headings:text-black prose-h1:text-5xl prose-h2:text-4xl prose-h3:text-3xl prose-h4:text-2xl prose-h5:text-xl prose-h6:text-lg dark:prose-headings:text-white">
{children}
</div>
)
}
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 l'ajouter à votre contenu MDX, telles que :
@next/mdx
vous permet d'utiliser des exports comme n'importe quel autre composant JavaScript :
export const metadata = {
author: 'John Doe',
}
# Article de blog
Les métadonnées peuvent maintenant être référencées en dehors du fichier MDX :
import BlogPost, { metadata } from '@/content/blog-post.mdx'
export default function Page() {
console.log('metadata: ', metadata)
//=> { author: 'John Doe' }
return <BlogPost />
}
import BlogPost, { metadata } from '@/content/blog-post.mdx'
export default function Page() {
console.log('metadata: ', metadata)
//=> { author: 'John Doe' }
return <BlogPost />
}
Un cas d'utilisation courant est lorsque vous souhaitez itérer sur une collection de MDX et extraire des données. Par exemple, créer une page d'index de blog à partir de tous les articles. Vous pouvez utiliser des packages comme le module fs
de Node ou globby pour lire un répertoire d'articles et extraire les métadonnées.
Bon à savoir :
- L'utilisation de
fs
,globby
, etc. ne peut se faire que côté serveur.- Consultez le Portfolio Starter Kit pour un exemple complet.
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
ou next.config.ts
comme fichier de configuration.
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Autoriser les extensions .mdx pour les fichiers
pageExtensions: ['js', 'jsx', 'md', '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: [],
},
})
// Combinez la configuration MDX et Next.js
export default withMDX(nextConfig)
Utilisation des plugins avec Turbopack
Pour utiliser des plugins avec Turbopack, mettez à jour vers la dernière version de @next/mdx
et spécifiez les noms des plugins sous forme de chaîne :
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
}
const withMDX = createMDX({
options: {
remarkPlugins: [],
rehypePlugins: [['rehype-katex', { strict: true, throwOnError: true }]],
},
})
export default withMDX(nextConfig)
Bon à savoir :
Les plugins remark et rehype sans options sérialisables ne peuvent pas encore être utilisés avec Turbopack, en raison de l'impossibilité de passer des fonctions JavaScript à Rust
MDX distant
Si vos fichiers ou contenu MDX se trouvent ailleurs, vous pouvez les récupérer dynamiquement sur le serveur. Ceci est utile pour le contenu stocké dans un CMS, une base de données ou ailleurs. Un package communautaire pour ce cas d'utilisation est next-mdx-remote-client
.
Bon à savoir : Procédez avec prudence. MDX est compilé en JavaScript et exécuté sur le serveur. Vous ne devez récupérer du contenu MDX que depuis une source de confiance, sinon cela peut conduire à une exécution de code à distance (RCE).
L'exemple suivant utilise next-mdx-remote-client
:
import { MDXRemote } from 'next-mdx-remote-client/rsc'
export default async function RemoteMdxPage() {
// Texte MDX - peut provenir d'une base de données, d'un CMS, d'une requête fetch, etc.
const res = await fetch('https://...')
const markdown = await res.text()
return <MDXRemote source={markdown} />
}
import { MDXRemote } from 'next-mdx-remote-client/rsc'
export default async function RemoteMdxPage() {
// Texte MDX - peut provenir d'une base de données, d'un CMS, d'une requête fetch, etc.
const res = await fetch('https://...')
const markdown = await res.text()
return <MDXRemote source={markdown} />
}
Naviguer vers la route /mdx-page-remote
devrait afficher votre MDX rendu.
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 similaire, mais pour le 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 d'en-têtes, 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 coulisses.
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
:
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})
mdxRs
accepte également un objet pour configurer la transformation des fichiers mdx.
module.exports = withMDX({
experimental: {
mdxRs: {
jsxRuntime?: string // Runtime JSX personnalisée
jsxImportSource?: string // Source d'import JSX personnalisée,
mdxType?: 'gfm' | 'commonmark' // Configure le type de syntaxe MDX utilisé pour l'analyse et la transformation
},
},
})