MarkdownとMDX
Markdownは、テキストをフォーマットするための軽量マークアップ言語です。プレーンテキスト構文で記述し、構造的に有効なHTMLに変換できます。ウェブサイトやブログのコンテンツ作成によく使用されます。
例えば次のように記述すると...
I **love** using [Next.js](https://nextjs.org/)
次のHTMLが出力されます:
<p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>
MDXはMarkdownの拡張版で、JSXを直接Markdownファイル内に記述できます。コンテンツ内に動的なインタラクティブ性を追加したり、Reactコンポーネントを埋め込む強力な方法です。
Next.jsは、アプリケーション内のローカルMDXコンテンツと、サーバー上で動的に取得されるリモートMDXファイルの両方をサポートしています。Next.jsプラグインは、MarkdownとReactコンポーネントをHTMLに変換する処理を行い、Server Components(App Routerのデフォルト)での使用もサポートします。
@next/mdx
@next/mdx
パッケージは、Next.jsがMarkdownとMDXを処理できるように設定するために使用されます。ローカルファイルからデータを取得し、/pages
または/app
ディレクトリ内で直接.mdx
拡張子のページを作成できます。
Next.jsでMDXを設定して使用する方法を見ていきましょう。
はじめに
MDXをレンダリングするために必要なパッケージをインストールします:
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
プロジェクトのルートにある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)
次に、/pages
ディレクトリ内に新しいMDXページを作成します:
your-project
├── pages
│ └── my-mdx-page.mdx
└── package.json
これで、MDXページ内でMarkdownを使用したり、Reactコンポーネントを直接インポートできます:
import { MyComponent } from 'my-components'
# 私のMDXページへようこそ!
これは**太字**と_斜体_のテキストです。
Markdownでのリスト:
- 一つ
- 二つ
- 三つ
Reactコンポーネントをチェック:
<MyComponent />
/my-mdx-page
ルートにアクセスすると、レンダリングされたMDXが表示されます。
リモートMDX
MarkdownやMDXファイルが別の場所にある場合、サーバー上で動的に取得できます。これは、別のローカルフォルダ、CMS、データベース、その他の場所に保存されているコンテンツに便利です。この用途で人気のあるコミュニティパッケージはnext-mdx-remote
です。
注意: 慎重に進めてください。MDXはJavaScriptにコンパイルされ、サーバー上で実行されます。信頼できるソースからのみMDXコンテンツを取得する必要があります。そうでない場合、リモートコード実行(RCE)につながる可能性があります。
次の例ではnext-mdx-remote
を使用しています:
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'
interface Props {
mdxSource: MDXRemoteSerializeResult
}
export default function RemoteMdxPage({ mdxSource }: Props) {
return <MDXRemote {...mdxSource} />
}
export async function getStaticProps() {
// MDXテキスト - ローカルファイル、データベース、CMS、fetchなどから取得可能
const res = await fetch('https:...')
const mdxText = await res.text()
const mdxSource = await serialize(mdxText)
return { props: { mdxSource } }
}
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'
export default function RemoteMdxPage({ mdxSource }) {
return <MDXRemote {...mdxSource} />
}
export async function getStaticProps() {
// MDXテキスト - ローカルファイル、データベース、CMS、fetchなどから取得可能
const res = await fetch('https:...')
const mdxText = await res.text()
const mdxSource = await serialize(mdxText)
return { props: { mdxSource } }
}
/my-mdx-page-remote
ルートにアクセスすると、レンダリングされたMDXが表示されます。
レイアウト
MDXページ周囲でレイアウトを共有するには、レイアウトコンポーネントを作成します:
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>
}
次に、レイアウトコンポーネントをMDXページにインポートし、MDXコンテンツをレイアウトでラップしてエクスポートします:
import MdxLayout from '../components/mdx-layout'
# 私のMDXページへようこそ!
export default function MDXPage({ children }) {
return <MdxLayout>{children}</MdxLayout>
}
RemarkとRehypeプラグイン
必要に応じて、MDXコンテンツを変換するためにremark
とrehype
プラグインを提供できます。
例えば、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({
// 必要に応じてMarkdownプラグインを追加
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
})
// MDXとNext.js設定を相互にラップ
export default withMDX(nextConfig)
フロントマター
フロントマターは、ページに関するデータを保存するために使用できるYAMLのようなキー/値のペアです。@next/mdx
はデフォルトでフロントマターをサポートしていませんが、MDXコンテンツにフロントマターを追加する多くの解決策があります:
@next/mdx
でページメタデータにアクセスするには、.mdx
ファイル内からmetadataオブジェクトをエクスポートできます:
export const metadata = {
author: 'John Doe',
}
# 私のMDXページ
カスタム要素
Markdownを使用する利点の1つは、ネイティブのHTML
要素にマッピングされるため、記述が速く直感的であることです:
これはMarkdownでのリストです:
- 一つ
- 二つ
- 三つ
上記は次のHTML
を生成します:
<p>これはMarkdownでのリストです:</p>
<ul>
<li>一つ</li>
<li>二つ</li>
<li>三つ</li>
</ul>
ウェブサイトやアプリケーションに独自の要素をスタイリングしたい場合、ショートコードを渡すことができます。これらはHTML
要素にマッピングする独自のカスタムコンポーネントです。
これを行うには、アプリケーションのルート(pages/
またはsrc/
の親フォルダ)にmdx-components.tsx
ファイルを作成し、カスタム要素を追加します:
import type { MDXComponents } from 'mdx/types'
import Image, { ImageProps } 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 as ImageProps)}
/>
),
...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,
}
}
詳細: MarkdownをHTMLに変換する方法
ReactはネイティブでMarkdownを理解しません。MarkdownプレーンテキストはまずHTMLに変換する必要があります。これはremark
とrehype
で実現できます。
remark
はMarkdown周辺のツールエコシステムです。rehype
はHTML用の同様のエコシステムです。例えば、次のコードスニペットはMarkdownを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) // Markdown 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コンパイラをサポートしています。このコンパイラはまだ実験的で、本番環境での使用は推奨されていません。新しいコンパイラを使用するには、withMDX
に渡すときにnext.config.js
を設定する必要があります:
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})