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.
Créez une nouvelle page MDX dans le répertoire /pages
:
my-project
|── mdx-components.(tsx/js)
├── pages
│ └── mdx-page.(mdx/md)
└── 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 /pages
et un fichier MDX où vous le souhaitez :
.
├── markdown/
│ └── welcome.(mdx/md)
├── pages/
│ └── mdx-page.(tsx/js)
├── 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.
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 autour des pages MDX, créez un composant de mise en page :
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>
}
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 MdxLayout from '../components/mdx-layout'
# Bienvenue sur ma page MDX !
export default function MDXPage({ children }) {
return <MdxLayout>{children}</MdxLayout>
}
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.
Pour partager un layout autour des pages MDX, créez un composant de layout :
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>
)
}
Ensuite, importez le composant de layout dans la page MDX, encadrez le contenu MDX avec le layout et exportez-le :
import MdxLayout from '../components/mdx-layout'
# Bienvenue sur ma page MDX !
export default function MDXPage({ children }) {
return <MdxLayout>{children}</MdxLayout>
}
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 {
serialize,
type SerializeResult,
} from 'next-mdx-remote-client/serialize'
import { MDXClient } from 'next-mdx-remote-client'
type Props = {
mdxSource: SerializeResult
}
export default function RemoteMdxPage({ mdxSource }: Props) {
if ('error' in mdxSource) {
// soit affichez une UI d'erreur, soit lancez `mdxSource.error`
}
return <MDXClient {...mdxSource} />
}
export async function getStaticProps() {
// 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 mdxText = await res.text()
const mdxSource = await serialize({ source: mdxText })
return { props: { mdxSource } }
}
import { serialize } from 'next-mdx-remote-client/serialize'
import { MDXClient } from 'next-mdx-remote-client'
export default function RemoteMdxPage({ mdxSource }) {
if ('error' in mdxSource) {
// soit affichez une UI d'erreur, soit lancez `mdxSource.error`
}
return <MDXClient {...mdxSource} />
}
export async function getStaticProps() {
// 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 mdxText = await res.text()
const mdxSource = await serialize({ source: mdxText })
return { props: { mdxSource } }
}
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
},
},
})
Liens utiles
Chargement à la demande
Chargez à la demande les bibliothèques importées et les composants React pour améliorer les performances de chargement globales de votre application.
Multi-Zones
Découvrez comment construire des micro-frontends en utilisant les Multi-Zones de Next.js pour déployer plusieurs applications Next.js sous un seul domaine.