Verwendung von Markdown und MDX in Next.js
Markdown ist eine leichtgewichtige Auszeichnungssprache zur Textformatierung. Sie ermöglicht es Ihnen, mit einfacher Text-Syntax zu schreiben und diese in strukturell valides HTML umzuwandeln. Sie wird häufig für das Schreiben von Inhalten auf Websites und Blogs verwendet.
Sie schreiben...
I **love** using [Next.js](https://nextjs.org/)
Ausgabe:
<p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>
MDX ist eine Erweiterung von Markdown, die es Ihnen ermöglicht, JSX direkt in Ihren Markdown-Dateien zu schreiben. Es ist eine leistungsstarke Möglichkeit, dynamische Interaktivität hinzuzufügen und React-Komponenten in Ihre Inhalte einzubetten.
Next.js kann sowohl lokale MDX-Inhalte innerhalb Ihrer Anwendung als auch remote MDX-Dateien unterstützen, die dynamisch auf dem Server abgerufen werden. Das Next.js-Plugin übernimmt die Transformation von Markdown und React-Komponenten in HTML, einschließlich der Unterstützung für die Verwendung in Server Components (Standard im App Router).
Gut zu wissen: Sehen Sie sich die Portfolio Starter Kit-Vorlage für ein vollständiges funktionierendes Beispiel an.
Abhängigkeiten installieren
Das @next/mdx
-Paket und verwandte Pakete werden verwendet, um Next.js so zu konfigurieren, dass es Markdown und MDX verarbeiten kann. Es bezieht Daten aus lokalen Dateien, sodass Sie Seiten mit der Erweiterung .md
oder .mdx
direkt in Ihrem /pages
- oder /app
-Verzeichnis erstellen können.
Installieren Sie diese Pakete, um MDX mit Next.js zu rendern:
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
next.config.mjs
konfigurieren
Aktualisieren Sie die next.config.mjs
-Datei im Stammverzeichnis Ihres Projekts, um die Verwendung von MDX zu konfigurieren:
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Konfigurieren Sie `pageExtensions`, um Markdown- und MDX-Dateien einzuschließen
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
// Optional: Fügen Sie hier weitere Next.js-Konfigurationen hinzu
}
const withMDX = createMDX({
// Fügen Sie hier gewünschte Markdown-Plugins hinzu
})
// MDX-Konfiguration mit Next.js-Konfiguration zusammenführen
export default withMDX(nextConfig)
Dies ermöglicht es .mdx
-Dateien, als Seiten, Routen oder Importe in Ihrer Anwendung zu fungieren.
Behandlung von .md
-Dateien
Standardmäßig kompiliert next/mdx
nur Dateien mit der Erweiterung .mdx
. Um .md
-Dateien mit webpack zu behandeln, aktualisieren Sie die extension
-Option:
const withMDX = createMDX({
extension: /\.(md|mdx)$/,
})
Gut zu wissen: Turbopack unterstützt derzeit die
extension
-Option nicht und daher auch keine.md
-Dateien.
Eine mdx-components.tsx
-Datei hinzufügen
Erstellen Sie eine mdx-components.tsx
(oder .js
)-Datei im Stammverzeichnis Ihres Projekts, um globale MDX-Komponenten zu definieren. Zum Beispiel auf derselben Ebene wie pages
oder app
oder innerhalb von src
, falls zutreffend.
import type { MDXComponents } from 'mdx/types'
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
}
}
export function useMDXComponents(components) {
return {
...components,
}
}
Gut zu wissen:
mdx-components.tsx
ist erforderlich, um@next/mdx
mit dem App Router zu verwenden, und funktioniert nicht ohne sie.- Erfahren Sie mehr über die
mdx-components.tsx
-Dateikonvention.- Erfahren Sie, wie Sie benutzerdefinierte Stile und Komponenten verwenden.
MDX rendern
Sie können MDX mit Next.js' dateibasiertem Routing oder durch Importieren von MDX-Dateien in andere Seiten rendern.
Dateibasiertes Routing verwenden
Bei der Verwendung von dateibasiertem Routing können Sie MDX-Seiten wie jede andere Seite verwenden.
Erstellen Sie eine neue MDX-Seite im /pages
-Verzeichnis:
my-project
|── mdx-components.(tsx/js)
├── pages
│ └── mdx-page.(mdx/md)
└── package.json
Sie können MDX in diesen Dateien verwenden und sogar React-Komponenten direkt in Ihrer MDX-Seite importieren:
import { MyComponent } from 'my-component'
# Willkommen auf meiner MDX-Seite!
Dies ist ein **fetter** und _kursiver_ Text.
Dies ist eine Liste in Markdown:
- Eins
- Zwei
- Drei
Schauen Sie sich meine React-Komponente an:
<MyComponent />
Das Navigieren zur /mdx-page
-Route sollte Ihre gerenderte MDX-Seite anzeigen.
Importe verwenden
Erstellen Sie eine neue Seite im /pages
-Verzeichnis und eine MDX-Datei an einem beliebigen Ort:
.
├── markdown/
│ └── welcome.(mdx/md)
├── pages/
│ └── mdx-page.(tsx/js)
├── mdx-components.(tsx/js)
└── package.json
Sie können MDX in diesen Dateien verwenden und sogar React-Komponenten direkt in Ihrer MDX-Seite importieren:
import { MyComponent } from 'my-component'
# Willkommen auf meiner MDX-Seite!
Dies ist ein **fetter** und _kursiver_ Text.
Dies ist eine Liste in Markdown:
- Eins
- Zwei
- Drei
Schauen Sie sich meine React-Komponente an:
<MyComponent />
Importieren Sie die MDX-Datei in die Seite, um den Inhalt anzuzeigen:
import Welcome from '@/markdown/welcome.mdx'
export default function Page() {
return <Welcome />
}
import Welcome from '@/markdown/welcome.mdx'
export default function Page() {
return <Welcome />
}
Das Navigieren zur /mdx-page
-Route sollte Ihre gerenderte MDX-Seite anzeigen.
Benutzerdefinierte Stile und Komponenten verwenden
Markdown wird beim Rendern auf native HTML-Elemente abgebildet. Zum Beispiel generiert das Schreiben des folgenden Markdowns:
## Dies ist eine Überschrift
Dies ist eine Liste in Markdown:
- Eins
- Zwei
- Drei
Das folgende HTML:
<h2>Dies ist eine Überschrift</h2>
<p>Dies ist eine Liste in Markdown:</p>
<ul>
<li>Eins</li>
<li>Zwei</li>
<li>Drei</li>
</ul>
Um Ihren Markdown zu gestalten, können Sie benutzerdefinierte Komponenten bereitstellen, die auf die generierten HTML-Elemente abgebildet werden. Stile und Komponenten können global, lokal und mit gemeinsamen Layouts implementiert werden.
Globale Stile und Komponenten
Das Hinzufügen von Stilen und Komponenten in mdx-components.tsx
betrifft alle MDX-Dateien in Ihrer Anwendung.
import type { MDXComponents } from 'mdx/types'
import Image, { ImageProps } from 'next/image'
// Diese Datei ermöglicht es Ihnen, benutzerdefinierte React-Komponenten
// für die Verwendung in MDX-Dateien bereitzustellen. Sie können jede
// React-Komponente importieren und verwenden, einschließlich Inline-Stilen,
// Komponenten aus anderen Bibliotheken und mehr.
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
// Ermöglicht die Anpassung eingebauter Komponenten, z.B. zum Hinzufügen von Stilen.
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'
// Diese Datei ermöglicht es Ihnen, benutzerdefinierte React-Komponenten
// für die Verwendung in MDX-Dateien bereitzustellen. Sie können jede
// React-Komponente importieren und verwenden, einschließlich Inline-Stilen,
// Komponenten aus anderen Bibliotheken und mehr.
export function useMDXComponents(components) {
return {
// Ermöglicht die Anpassung eingebauter Komponenten, z.B. zum Hinzufügen von Stilen.
h1: ({ children }) => (
<h1 style={{ color: 'red', fontSize: '48px' }}>{children}</h1>
),
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...props}
/>
),
...components,
}
}
Lokale Stile und Komponenten
Sie können lokale Stile und Komponenten auf bestimmte Seiten anwenden, indem Sie sie in importierte MDX-Komponenten übergeben. Diese werden mit globalen Stilen und Komponenten zusammengeführt und überschreiben diese.
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} />
}
Gemeinsame Layouts
Um ein Layout um MDX-Seiten herum zu teilen, erstellen Sie eine Layout-Komponente:
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Erstellen Sie hier gemeinsame Layouts oder Stile
return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MdxLayout({ children }) {
// Erstellen Sie hier gemeinsame Layouts oder Stile
return <div style={{ color: 'blue' }}>{children}</div>
}
Importieren Sie dann die Layout-Komponente in die MDX-Seite, wickeln Sie den MDX-Inhalt in das Layout ein und exportieren Sie es:
import MdxLayout from '../components/mdx-layout'
# Willkommen auf meiner MDX-Seite!
export default function MDXPage({ children }) {
return <MdxLayout>{children}</MdxLayout>
}
Verwendung des Tailwind-Typografie-Plugins
Wenn Sie Tailwind zur Gestaltung Ihrer Anwendung verwenden, ermöglicht Ihnen das Plugin @tailwindcss/typography
, Ihre Tailwind-Konfiguration und -Stile in Ihren Markdown-Dateien wiederzuverwenden.
Das Plugin fügt eine Reihe von prose
-Klassen hinzu, mit denen Sie typografische Stile auf Inhaltsblöcke anwenden können, die aus Quellen wie Markdown stammen.
Installieren Sie Tailwind-Typografie und verwenden Sie es mit gemeinsamen Layouts, um die gewünschte prose
-Klasse hinzuzufügen.
Um ein Layout für MDX-Seiten zu teilen, erstellen Sie eine Layout-Komponente:
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Erstellen Sie hier gemeinsame Layouts oder Stile
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 }) {
// Erstellen Sie hier gemeinsame Layouts oder Stile
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>
)
}
Importieren Sie dann die Layout-Komponente in die MDX-Seite, umschließen Sie den MDX-Inhalt mit dem Layout und exportieren Sie es:
import MdxLayout from '../components/mdx-layout'
# Willkommen auf meiner MDX-Seite!
export default function MDXPage({ children }) {
return <MdxLayout>{children}</MdxLayout>
}
Frontmatter
Frontmatter ist eine YAML-ähnliche Schlüssel/Wert-Paarung, die verwendet werden kann, um Daten über eine Seite zu speichern. @next/mdx
unterstützt Frontmatter nicht standardmäßig, aber es gibt viele Lösungen, um Frontmatter zu Ihrem MDX-Inhalt hinzuzufügen, wie z.B.:
@next/mdx
erlaubt Ihnen jedoch, Exporte wie bei jeder anderen JavaScript-Komponente zu verwenden:
export const metadata = {
author: 'John Doe',
}
# Blogbeitrag
Die Metadaten können nun außerhalb der MDX-Datei referenziert werden:
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 />
}
Ein häufiger Anwendungsfall hierfür ist, wenn Sie über eine Sammlung von MDX-Dateien iterieren und Daten extrahieren möchten. Zum Beispiel das Erstellen einer Blog-Indexseite aus allen Blogbeiträgen. Sie können Pakete wie Node's fs
-Modul oder globby verwenden, um ein Verzeichnis mit Beiträgen zu lesen und die Metadaten zu extrahieren.
Gut zu wissen:
- Die Verwendung von
fs
,globby
usw. ist nur serverseitig möglich.- Sehen Sie sich die Portfolio Starter Kit-Vorlage für ein vollständiges funktionierendes Beispiel an.
remark- und rehype-Plugins
Sie können optional remark- und rehype-Plugins bereitstellen, um den MDX-Inhalt zu transformieren.
Beispielsweise können Sie remark-gfm
verwenden, um GitHub Flavored Markdown zu unterstützen.
Da das remark- und rehype-Ökosystem nur ESM unterstützt, müssen Sie next.config.mjs
oder next.config.ts
als Konfigurationsdatei verwenden.
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Erlaube .mdx-Erweiterungen für Dateien
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
// Optional: Fügen Sie hier weitere Next.js-Konfigurationen hinzu
}
const withMDX = createMDX({
// Fügen Sie hier gewünschte Markdown-Plugins hinzu
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
})
// Kombinieren Sie MDX- und Next.js-Konfiguration
export default withMDX(nextConfig)
Verwendung von Plugins mit Turbopack
Um Plugins mit Turbopack zu verwenden, aktualisieren Sie auf die neueste Version von @next/mdx
und geben Sie Plugin-Namen als Zeichenkette an:
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)
Gut zu wissen:
remark- und rehype-Plugins ohne serialisierbare Optionen können noch nicht mit Turbopack verwendet werden, aufgrund der Unfähigkeit, JavaScript-Funktionen an Rust zu übergeben
Remote-MDX
Wenn Ihre MDX-Dateien oder -Inhalte an einem anderen Ort gespeichert sind, können Sie sie dynamisch auf dem Server abrufen. Dies ist nützlich für Inhalte, die in einem CMS, einer Datenbank oder anderswo gespeichert sind. Ein Community-Paket für diesen Anwendungsfall ist next-mdx-remote-client
.
Gut zu wissen: Bitte gehen Sie vorsichtig vor. MDX wird in JavaScript kompiliert und auf dem Server ausgeführt. Sie sollten MDX-Inhalte nur aus einer vertrauenswürdigen Quelle abrufen, da dies sonst zu Remote-Code-Ausführung (RCE) führen kann.
Das folgende Beispiel verwendet 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) {
// Entweder Fehler-UI rendern oder `mdxSource.error` werfen
}
return <MDXClient {...mdxSource} />
}
export async function getStaticProps() {
// MDX-Text - kann aus einer Datenbank, einem CMS, einem Fetch-Aufruf oder anderswo stammen...
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) {
// Entweder Fehler-UI rendern oder `mdxSource.error` werfen
}
return <MDXClient {...mdxSource} />
}
export async function getStaticProps() {
// MDX-Text - kann aus einer Datenbank, einem CMS, einem Fetch-Aufruf oder anderswo stammen...
const res = await fetch('https:...')
const mdxText = await res.text()
const mdxSource = await serialize({ source: mdxText })
return { props: { mdxSource } }
}
Das Navigieren zur Route /mdx-page-remote
sollte Ihren gerenderten MDX-Inhalt anzeigen.
Vertiefung: Wie wird Markdown in HTML umgewandelt?
React versteht Markdown nicht nativ. Der Markdown-Text muss zunächst in HTML umgewandelt werden. Dies kann mit remark
und rehype
erreicht werden.
remark
ist ein Ökosystem von Tools rund um Markdown. rehype
ist das gleiche, aber für HTML. Der folgende Codeausschnitt wandelt beispielsweise Markdown in HTML um:
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) // In Markdown-AST umwandeln
.use(remarkRehype) // In HTML-AST transformieren
.use(rehypeSanitize) // HTML-Eingabe bereinigen
.use(rehypeStringify) // AST in serialisiertes HTML umwandeln
.process('Hello, Next.js!')
console.log(String(file)) // <p>Hello, Next.js!</p>
}
Das remark
- und rehype
-Ökosystem enthält Plugins für Syntax-Hervorhebung, verlinkte Überschriften, Generierung eines Inhaltsverzeichnisses und mehr.
Wenn Sie @next/mdx
wie oben gezeigt verwenden, müssen Sie remark
oder rehype
nicht direkt verwenden, da dies für Sie erledigt wird. Wir beschreiben es hier, um ein tieferes Verständnis dafür zu vermitteln, was das @next/mdx
-Paket im Hintergrund tut.
Verwendung des Rust-basierten MDX-Compilers (experimentell)
Next.js unterstützt einen neuen MDX-Compiler, der in Rust geschrieben wurde. Dieser Compiler ist noch experimentell und wird nicht für den Produktionseinsatz empfohlen. Um den neuen Compiler zu verwenden, müssen Sie next.config.js
konfigurieren, wenn Sie es an withMDX
übergeben:
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})
mdxRs
akzeptiert auch ein Objekt, um die Transformation von MDX-Dateien zu konfigurieren.
module.exports = withMDX({
experimental: {
mdxRs: {
jsxRuntime?: string // Benutzerdefinierte JSX-Runtime
jsxImportSource?: string // Benutzerdefinierte JSX-Importquelle,
mdxType?: 'gfm' | 'commonmark' // Konfiguriert, welche Art von MDX-Syntax zum Parsen & Transformieren verwendet wird
},
},
})