Markdown und MDX

Markdown ist eine leichtgewichtige Auszeichnungssprache zur Textformatierung. Sie ermöglicht das Schreiben mit einfacher Textsyntax und die Konvertierung in strukturell valides HTML. Es wird häufig für das Verfassen 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 verwenden. Es ist eine leistungsstarke Methode, um dynamische Interaktivität hinzuzufügen und React-Komponenten in Ihre Inhalte einzubetten.

Next.js unterstützt sowohl lokale MDX-Inhalte innerhalb Ihrer Anwendung als auch remote MDX-Dateien, 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).

@next/mdx

Das @next/mdx-Paket wird verwendet, um Next.js für die Verarbeitung von Markdown und MDX zu konfigurieren. Es bezieht Daten aus lokalen Dateien, sodass Sie Seiten mit der Erweiterung .mdx direkt in Ihrem /pages- oder /app-Verzeichnis erstellen können.

Gehen wir durch, wie Sie MDX mit Next.js konfigurieren und verwenden.

Erste Schritte

Installieren Sie die benötigten Pakete, um MDX zu rendern:

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

Erstellen Sie eine Datei mdx-components.tsx im Stammverzeichnis Ihrer Anwendung (src/ oder das übergeordnete Verzeichnis von app/):

Gut zu wissen: mdx-components.tsx ist erforderlich, um MDX mit dem App Router zu verwenden, und funktioniert nicht ohne sie.

import type { MDXComponents } from 'mdx/types'

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
  }
}
export function useMDXComponents(components) {
  return {
    ...components,
  }
}

Aktualisieren Sie die Datei next.config.js im Stammverzeichnis Ihres Projekts, um die Verwendung von MDX zu konfigurieren:

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

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Konfigurieren Sie `pageExtensions`, um MDX-Dateien einzubeziehen
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // Optional: Fügen Sie weitere Next.js-Konfigurationen hinzu
}

module.exports = withMDX(nextConfig)

Erstellen Sie dann eine neue MDX-Seite im /app-Verzeichnis:

  your-project
  ├── app
  │   └── my-mdx-page
  │       └── page.mdx
  └── package.json

Jetzt können Sie Markdown verwenden und React-Komponenten direkt in Ihrer MDX-Seite importieren:

import { MyComponent } from 'my-components'

# Willkommen auf meiner MDX-Seite!

Dies ist ein **fetter** und _kursiver_ Text.

Dies ist eine Liste in Markdown:

- Eins
- Zwei
- Drei

Sehen Sie sich meine React-Komponente an:

<MyComponent />

Wenn Sie zur Route /my-mdx-page navigieren, sollte Ihr gerenderter MDX-Inhalt angezeigt werden.

Remote MDX

Wenn sich Ihre Markdown- oder MDX-Dateien oder -Inhalte an einem anderen Ort befinden, können Sie sie dynamisch auf dem Server abrufen. Dies ist nützlich für Inhalte, die in einem separaten lokalen Ordner, einem CMS, einer Datenbank oder anderswo gespeichert sind. Ein beliebiges Community-Paket für diesen Anwendungsfall ist next-mdx-remote.

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:

import { MDXRemote } from 'next-mdx-remote/rsc'

export default async function RemoteMdxPage() {
  // MDX-Text - kann aus einer lokalen Datei, Datenbank, CMS, Fetch usw. stammen...
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}
import { MDXRemote } from 'next-mdx-remote/rsc'

export default async function RemoteMdxPage() {
  // MDX-Text - kann aus einer lokalen Datei, Datenbank, CMS, Fetch usw. stammen...
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}

Wenn Sie zur Route /my-mdx-page-remote navigieren, sollte Ihr gerenderter MDX-Inhalt angezeigt werden.

Layouts

Um ein Layout über mehrere MDX-Seiten hinweg zu teilen, können Sie die integrierte Layout-Unterstützung mit dem App Router verwenden.

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>
}

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 als Konfigurationsdatei verwenden.

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

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Konfigurieren Sie `pageExtensions`, um MDX-Dateien einzubeziehen
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // Optional: Fügen Sie 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)

Frontmatter

Frontmatter ist eine YAML-ähnliche Schlüssel/Wert-Paarung, die verwendet werden kann, um Metadaten über eine Seite zu speichern. @next/mdx unterstützt Frontmatter nicht standardmäßig, obwohl es viele Lösungen gibt, um Frontmatter zu Ihren MDX-Inhalten hinzuzufügen, wie z.B.:

Um Seitenmetadaten mit @next/mdx abzurufen, können Sie ein Metadaten-Objekt aus der .mdx-Datei exportieren:

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

# Meine MDX-Seite

Benutzerdefinierte Elemente

Ein angenehmer Aspekt der Verwendung von Markdown ist, dass es auf native HTML-Elemente abgebildet wird, was das Schreiben schnell und intuitiv macht:

Dies ist eine Liste in Markdown:

- Eins
- Zwei
- Drei

Das obige Beispiel generiert das folgende HTML:

<p>Dies ist eine Liste in Markdown:</p>

<ul>
  <li>Eins</li>
  <li>Zwei</li>
  <li>Drei</li>
</ul>

Wenn Sie Ihre eigenen Elemente für ein individuelles Erscheinungsbild Ihrer Website oder Anwendung gestalten möchten, können Sie Shortcodes verwenden. Dies sind Ihre eigenen benutzerdefinierten Komponenten, die auf HTML-Elemente abgebildet werden.

Öffnen Sie dazu die Datei mdx-components.tsx im Stammverzeichnis Ihrer Anwendung und fügen Sie benutzerdefinierte Elemente hinzu:

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 usw.

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    // Ermöglicht die Anpassung integrierter Komponenten, z.B. für Styling.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{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 usw.

export function useMDXComponents(components) {
  return {
    // Ermöglicht die Anpassung integrierter Komponenten, z.B. für Styling.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...props}
      />
    ),
    ...components,
  }
}

Vertiefung: Wie wird Markdown in HTML transformiert?

React versteht Markdown nicht nativ. Der Markdown-Text muss zunächst in HTML transformiert werden. Dies kann mit remark und rehype erreicht werden.

remark ist ein Ökosystem von Tools rund um Markdown. rehype ist dasselbe, aber für HTML. Der folgende Codeausschnitt transformiert beispielsweise Markdown in 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) // In Markdown-AST konvertieren
    .use(remarkRehype) // In HTML-AST transformieren
    .use(rehypeSanitize) // HTML-Eingabe bereinigen
    .use(rehypeStringify) // AST in serialisiertes HTML konvertieren
    .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 nicht für den Produktionseinsatz empfohlen. Um den neuen Compiler zu verwenden, müssen Sie next.config.js konfigurieren, wenn Sie sie an withMDX übergeben:

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