Lang x Lang

Markdown and MDX

Markdown はテキストをフォーマットするための軽量マークアップ言語です。それはあなたがプレーンテキストの syntax を使って書き、それを構造的に有効な HTML に変換することを可能にします。それは一般的にウェブサイトやブログのコンテンツを書くために使用されます。

あなたは書きます...

I **love** using <a href="https://nextjs.org/" target="_blank" rel="nofollow noopener">Next.js <FiExternalLink /></a>

Output:

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

MDX はマークダウンのスーパーセットで、マークダウンファイルに直接JSX を書くことができます。これは、コンテンツ内に ReactComponent を埋め込んだり、dynamic なインタラクティビティを追加するための強力な方法です。

Next.js は、アプリケーション内のローカル MDX コンテンツと、server で動的に取得されるリモート MDX ファイルの両方をサポートできます。 Next.js plugin はマークダウンと ReactComponent を HTML に変換する処理を行い、Server Components(App Router の default)での使用をサポートします。

@next/mdx

@next/mdxパッケージは、Next.js がマークダウンと MDX を処理できるように設定するために使用されます。それはローカルファイルからデータを取得し/pagesまたは/appディレクトリに直接.mdx拡張子のページを作成することができます。

Next.js を使用して MDX を設定し、使用する方法を一緒に見ていきましょう。

Getting Started

render MDX に必要なパッケージをインストールします:

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

あなたのアプリケーションの root (src/ または app/ の親フォルダー)に mdx-components.tsx ファイルを作成してください:

Good to know: mdx-components.tsxは App Router で MDX を使用するためには必要で、それなしでは動作しません。

mdx-components.tsx
import type { MDXComponents } from 'mdx/types'

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

next.config.js ファイルをあなたのプロジェクトの root に更新して、それを MDX を使用するように設定します:

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

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Configure `pageExtensions` to include MDX files
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // Optionally, add any other Next.js config below
}

module.exports = withMDX(nextConfig)

次に、/appディレクトリ内に新しい MDX ページを作成します:

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

これで、MDX ページ内で直接 markdown と import ReactComponent を使用できるようになりました:

import { MyComponent } from "my-components";

# Welcome to my MDX page!

This is some **bold** and _italics_ text.

This is a list in markdown:

- One
- Two
- Three

Checkout my React component:

<MyComponent />

/my-mdx-page への route で移動すると、レンダリングされた MDX が display されます。

Remote MDX

あなたのマークダウンまたは MDX ファイルやコンテンツがどこか別の場所にある場合、それを server 上で動的に fetch できます。これは、別のローカルフォルダ、CMS、データベース、または他の場所に保存されているコンテンツに役立ちます。この目的に役立つ人気のコミュニティパッケージはnext-mdx-remote です。

Good to know: 注意して進めてください。MDX は JavaScript にコンパイルされ、 server 上で実行されます。信頼できる source からのみ MDX コンテンツを fetch すべきで、そうでないとリモート code 実行(RCE)につながる可能性があります。

次の例ではnext-mdx-remoteを使用します:

app/my-mdx-page-remote/page.tsx
import { MDXRemote } from 'next-mdx-remote/rsc'

export default async function RemoteMdxPage() {
  // MDX text - can be from a local file, database, CMS, fetch, anywhere...
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}
app/my-mdx-page-remote/page.js
import { MDXRemote } from 'next-mdx-remote/rsc'

export default async function RemoteMdxPage() {
  // MDX text - can be from a local file, database, CMS, fetch, anywhere...
  const res = await fetch('https://...')
  const markdown = await res.text()
  return <MDXRemote source={markdown} />
}

/my-mdx-page-remoteへの route に移動すると、rendering された MDX が表示されるはずです。

Layouts

MDX ページ間で layout を共有するには、App Router と共にbuilt-in layouts supportを使用することができます。

app/my-mdx-page/layout.tsx
export default function MdxLayout({ children }: { children: React.ReactNode }) {
  // Create any shared layout or styles here
  return <div style={{ color: 'blue' }}>{children}</div>
}
app/my-mdx-page/layout.js
export default function MdxLayout({ children }) {
  // Create any shared layout or styles here
  return <div style={{ color: 'blue' }}>{children}</div>
}

Remark and Rehype Plugins

必要に応じて、MDX コンテンツを transform するためのremarkおよびrehypeプラグインを提供することができます。

たとえば、GitHub フレーバーのマークダウンをサポートするためにremark-gfmを使用することができます。

remarkrehypeエコシステムが ESM のみなので、設定ファイルとしてnext.config.mjsを使用する必要があります。

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

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Configure `pageExtensions`` to include MDX files
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // Optionally, add any other Next.js config below
}

const withMDX = createMDX({
  // Add markdown plugins here, as desired
  options: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [],
  },
})

// Merge MDX config with Next.js config
export default withMDX(nextConfig)

Frontmatter

フロントマターは、ページに関するデータを保存するために使用できる YAML のようなキー/値のペアリングです。@next/mdxは、default ではフロントマターをサポートしませんが、MDX コンテンツにフロントマターを追加するための多くのソリューションが存在します。例えば:

@next/mdxを使用してページ metadata にアクセスするには、 .mdxファイル内から metadata object を export できます:

export const metadata = {
  author: "John Doe",
};

# My MDX page

Custom Elements

マークダウンを使用する楽しい側面の一つは、それがネイティブのHTML要素にマッピングされ、迅速かつ直感的な文章作成を可能にすることです:

This is a list in markdown:

- One
- Two
- Three

上記は次のHTMLを生成します:

<p>This is a list in markdown:</p>

<ul>
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>

あなたが自分のウェブサイトやアプリケーションにカスタム感を持たせたいと思ったときに、 shortcode を使用できます。これらは、あなた自身のカスタム components で、HTML要素にマップします。

これを行うには、アプリケーションの root にあるmdx-components.tsxファイルを開き、カスタム要素を追加します:

mdx-components.tsx
import type { MDXComponents } from 'mdx/types'
import Image, { ImageProps } from 'next/image'

// This file allows you to provide custom React components
// to be used in MDX files. You can import and use any
// React component you want, including inline styles,
// components from other libraries, and more.

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    // Allows customizing built-in components, e.g. to add styling.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...(props as ImageProps)}
      />
    ),
    ...components,
  }
}
mdx-components.js
import Image from 'next/image'

// This file allows you to provide custom React components
// to be used in MDX files. You can import and use any
// React component you want, including inline styles,
// components from other libraries, and more.

export function useMDXComponents(components) {
  return {
    // Allows customizing built-in components, e.g. to add styling.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...props}
      />
    ),
    ...components,
  }
}

Deep Dive: How do you transform markdown into HTML?

React はネイティブでマークダウンを理解することができません。マークダウンのプレーンテキストは、まず HTML に変換する必要があります。これはremarkrehypeを使用して達成することができます。

remarkはマークダウンを中心としたツールのエコシステムです。rehypeも同様で、これは HTML のためのものです。例えば、次の code スニペットはマークダウンを 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) // Convert into markdown AST
    .use(remarkRehype) // Transform to HTML AST
    .use(rehypeSanitize) // Sanitize HTML input
    .use(rehypeStringify) // Convert AST into serialized HTML
    .process("Hello, Next.js!");

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

remark および rehype エコシステムには、syntax ハイライト 見出しのリンク 目次の生成 などのプラグインが含まれています。

上記のように@next/mdxを使用する際、remarkrehypeを直接使用する必要はありません。なぜなら、それらはあなたのために処理されているからです。ここでは、@next/mdxパッケージが裏側で何を行っているのかをより深く理解するために説明しています。

Using the Rust-based MDX compiler (Experimental)

Next.js は、Rust で書かれた新しい MDX compiler をサポートしています。この compiler はまだ実験段階であり、production での使用は推奨されていません。新しい compiler を使用するには、 withMDX に渡すときに next.config.jsを設定する必要があります。

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

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use