MDX
Markdownは、テキストをフォーマットするための軽量マークアップ言語です。プレーンテキスト構文で記述し、構造的に有効なHTMLに変換できます。ウェブサイトやブログのコンテンツ作成によく使用されます。
次のように記述します...
I **love** using [Next.js](https://nextjs.org/)
出力:
<p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>
MDXはMarkdownの拡張で、JSXをマークダウンファイル内で直接記述できます。動的なインタラクティブ性を追加したり、Reactコンポーネントをコンテンツ内に埋め込む強力な方法です。
Next.jsは、アプリケーション内のローカルMDXコンテンツと、サーバー上で動的に取得するリモートMDXファイルの両方をサポートしています。Next.jsプラグインは、マークダウンとReactコンポーネントをHTMLに変換する処理を行い、サーバーコンポーネント(App Routerのデフォルト)での使用もサポートします。
@next/mdx
@next/mdx
パッケージは、Next.jsがマークダウンとMDXを処理できるように設定するために使用されます。ローカルファイルからデータを取得し、/pages
または/app
ディレクトリ内で直接.mdx
拡張子のページを作成できます。
Next.jsでMDXを設定して使用する方法を見ていきましょう。
はじめに
MDXをレンダリングするために必要なパッケージをインストールします:
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
アプリケーションのルート(app/
またはsrc/
の親フォルダ)にmdx-components.tsx
ファイルを作成します:
補足: App RouterでMDXを使用するには
mdx-components.tsx
が必須であり、これがないと動作しません。
import type { MDXComponents } from 'mdx/types'
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
}
}
export function useMDXComponents(components) {
return {
...components,
}
}
プロジェクトのルートにあるnext.config.js
ファイルを更新してMDXを使用するように設定します:
const withMDX = require('@next/mdx')()
/** @type {import('next').NextConfig} */
const nextConfig = {
// MDXファイルを含めるように`pageExtensions`を設定
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
// オプションで他のNext.js設定を追加
}
module.exports = withMDX(nextConfig)
次に、/app
ディレクトリ内に新しいMDXページを作成します:
your-project
├── app
│ └── my-mdx-page
│ └── page.mdx
└── package.json
これで、MDXページ内でマークダウンを使用したり、Reactコンポーネントを直接インポートできます:
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
ルートにアクセスすると、レンダリングされたMDXが表示されます。
リモートMDX
マークダウンやMDXファイルが他の場所にある場合、サーバー上で動的に取得できます。これは、別のローカルフォルダ、CMS、データベース、その他の場所に保存されているコンテンツに便利です。
MDXコンテンツを取得するための人気のあるコミュニティパッケージは次のとおりです:
注意: 慎重に進めてください。MDXはJavaScriptにコンパイルされ、サーバー上で実行されます。信頼できるソースからのみMDXコンテンツを取得する必要があります。そうしないと、リモートコード実行(RCE)につながる可能性があります。
次の例ではnext-mdx-remote
を使用しています:
import { MDXRemote } from 'next-mdx-remote/rsc'
export default async function RemoteMdxPage() {
// MDXテキスト - ローカルファイル、データベース、CMS、フェッチなどから取得可能
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テキスト - ローカルファイル、データベース、CMS、フェッチなどから取得可能
const res = await fetch('https://...')
const markdown = await res.text()
return <MDXRemote source={markdown} />
}
/my-mdx-page-remote
ルートにアクセスすると、レンダリングされたMDXが表示されます。
レイアウト
MDXページ間でレイアウトを共有するには、App Routerの組み込みレイアウトサポートを使用できます。
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// 共有レイアウトやスタイルを作成
return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MdxLayout({ children }) {
// 共有レイアウトやスタイルを作成
return <div style={{ color: 'blue' }}>{children}</div>
}
RemarkとRehypeプラグイン
オプションでremark
とrehype
プラグインを提供して、MDXコンテンツを変換できます。
例えば、remark-gfm
を使用してGitHub Flavored Markdownをサポートできます。
remark
とrehype
エコシステムはESMのみのため、設定ファイルとしてnext.config.mjs
を使用する必要があります。
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// MDXファイルを含めるように`pageExtensions`を設定
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
// オプションで他のNext.js設定を追加
}
const withMDX = createMDX({
// 必要に応じてマークダウンプラグインを追加
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
})
// MDX設定をNext.js設定とマージ
export default withMDX(nextConfig)
フロントマター
フロントマターは、ページに関するデータを保存するために使用できるYAMLのようなキー/値のペアリングです。@next/mdx
はデフォルトでフロントマターをサポートしていませんが、MDXコンテンツにフロントマターを追加する多くのソリューションがあります:
@next/mdx
でページメタデータにアクセスするには、.mdx
ファイル内からmetaオブジェクトをエクスポートできます:
export const meta = {
author: 'John Doe',
}
# My MDX page
カスタム要素
マークダウンを使用する利点の1つは、ネイティブの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>
ウェブサイトやアプリケーションに独自のスタイルを適用したい場合、ショートコードを渡すことができます。これらはHTML
要素にマッピングする独自のカスタムコンポーネントです。
これを行うには、アプリケーションのルートにあるmdx-components.tsx
ファイルを開き、カスタム要素を追加します:
import type { MDXComponents } from 'mdx/types'
import Image from 'next/image'
// このファイルでは、MDXファイルで使用するカスタムReactコンポーネントを提供できます。
// インラインスタイル、他のライブラリからのコンポーネントなど、
// 任意のReactコンポーネントをインポートして使用できます。
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
// 組み込みコンポーネントをカスタマイズ(スタイル追加など)
h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...props}
/>
),
...components,
}
}
import Image from 'next/image'
// このファイルでは、MDXファイルで使用するカスタムReactコンポーネントを提供できます。
// インラインスタイル、他のライブラリからのコンポーネントなど、
// 任意のReactコンポーネントをインポートして使用できます。
export function useMDXComponents(components) {
return {
// 組み込みコンポーネントをカスタマイズ(スタイル追加など)
h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...props}
/>
),
...components,
}
}
詳細: マークダウンをHTMLに変換する方法
Reactはネイティブでマークダウンを理解しません。マークダウンプレーンテキストはまずHTMLに変換される必要があります。これはremark
とrehype
で実現できます。
remark
はマークダウン周辺のツールエコシステムです。rehype
はHTML用の同様のエコシステムです。例えば、次のコードスニペットはマークダウンを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) // マークダウンASTに変換
.use(remarkRehype) // HTML ASTに変換
.use(rehypeSanitize) // HTML入力をサニタイズ
.use(rehypeStringify) // ASTをシリアライズされたHTMLに変換
.process('Hello, Next.js!')
console.log(String(file)) // <p>Hello, Next.js!</p>
}
remark
とrehype
エコシステムには、シンタックスハイライト、見出しリンク、目次生成などのプラグインが含まれます。
上記のように@next/mdx
を使用する場合、remark
やrehype
を直接使用する必要はありません。これらは自動的に処理されます。ここでは@next/mdx
パッケージが内部で何を行っているかを深く理解するために説明しています。
RustベースのMDXコンパイラの使用(実験的)
Next.jsはRustで書かれた新しいMDXコンパイラをサポートしています。このコンパイラはまだ実験的であり、本番環境での使用は推奨されません。新しいコンパイラを使用するには、next.config.js
をwithMDX
に渡すときに設定する必要があります:
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})