MarkdownとMDX
Markdownは、テキストをフォーマットするために使用される軽量なマークアップ言語です。プレーンテキストの構文を使用して記述し、構造的に有効なHTMLに変換することができます。ウェブサイトやブログでコンテンツを記述する際によく使用されます。
記述例
I **love** using [Next.js](https://nextjs.dokyumento.jp/)
出力
<p>I <strong>love</strong> using <a href="https://nextjs.dokyumento.jp/">Next.js</a></p>
MDXは、マークダウンのスーパーセットであり、マークダウンファイル内で直接JSXを記述できます。これは、動的なインタラクティブ性を追加し、コンテンツ内にReactコンポーネントを埋め込むための強力な方法です。
Next.jsは、アプリケーション内のローカルMDXコンテンツと、サーバーで動的にフェッチされるリモートMDXファイルの両方をサポートできます。Next.jsプラグインは、マークダウンとReactコンポーネントをHTMLに変換する処理を行い、サーバーコンポーネント (App Routerのデフォルト) での使用もサポートします。
補足: 完全な動作例については、ポートフォリオスターターキットテンプレートをご覧ください。
依存関係のインストール
@next/mdx
パッケージおよび関連パッケージは、Next.jsがマークダウンとMDXを処理できるように設定するために使用されます。ローカルファイルからデータを取得するため、.md
または.mdx
拡張子を持つページを/pages
または/app
ディレクトリ内に直接作成できます。
Next.jsでMDXをレンダリングするには、これらのパッケージをインストールしてください
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
next.config.mjs
の設定
プロジェクトのルートにあるnext.config.mjs
ファイルを更新して、MDXを使用するように設定します
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Configure `pageExtensions` to include markdown and MDX files
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
// Optionally, add any other Next.js config below
}
const withMDX = createMDX({
// Add markdown plugins here, as desired
})
// Merge MDX config with Next.js config
export default withMDX(nextConfig)
これにより、.md
および.mdx
ファイルがアプリケーションのページ、ルート、またはインポートとして機能できるようになります。
mdx-components.tsx
ファイルの追加
プロジェクトのルート (例: pages
またはapp
と同じレベル、または該当する場合はsrc
内) にmdx-components.tsx
(または.js
) ファイルを作成し、グローバルMDXコンポーネントを定義します。
import type { MDXComponents } from 'mdx/types'
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
}
}
補足:
mdx-components.tsx
は、App Routerで@next/mdx
を使用するために必須であり、これがないと動作しません。mdx-components.tsx
ファイル規約について詳しく学ぶ。- カスタムスタイルとコンポーネントの使用方法について学ぶ。
MDXのレンダリング
MDXは、Next.jsのファイルベースルーティングを使用するか、MDXファイルを他のページにインポートすることでレンダリングできます。
ファイルベースルーティングの使用
ファイルベースルーティングを使用する場合、MDXページを他のページと同様に利用できます。
App Routerアプリでは、これにはメタデータを使用する機能も含まれます。
/app
ディレクトリ内に新しいMDXページを作成します
my-project
├── app
│ └── mdx-page
│ └── page.(mdx/md)
|── mdx-components.(tsx/js)
└── package.json
これらのファイルでMDXを使用でき、MDXページ内で直接Reactコンポーネントをインポートすることもできます。
import { MyComponent } from 'my-component'
# 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 />
/mdx-page
ルートに移動すると、レンダリングされたMDXページが表示されるはずです。
インポートの使用
/app
ディレクトリ内に新しいページを作成し、MDXファイルを好きな場所に作成します
.
├── app/
│ └── mdx-page/
│ └── page.(tsx/js)
├── markdown/
│ └── welcome.(mdx/md)
├── mdx-components.(tsx/js)
└── package.json
これらのファイルでMDXを使用でき、MDXページ内で直接Reactコンポーネントをインポートすることもできます。
コンテンツを表示するために、MDXファイルをページ内にインポートします
import Welcome from '@/markdown/welcome.mdx'
export default function Page() {
return <Welcome />
}
/mdx-page
ルートに移動すると、レンダリングされたMDXページが表示されるはずです。
動的インポートの使用
MDXファイルのファイルシステムルーティングを使用する代わりに、動的なMDXコンポーネントをインポートできます。
例えば、別のディレクトリからMDXコンポーネントをロードする動的なルートセグメントを持つことができます。

generateStaticParams
は、提供されたルートをプリレンダリングするために使用できます。dynamicParams
をfalse
に設定することで、generateStaticParams
で定義されていないルートにアクセスすると404エラーになります。
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const { default: Post } = await import(`@/content/${slug}.mdx`)
return <Post />
}
export function generateStaticParams() {
return [{ slug: 'welcome' }, { slug: 'about' }]
}
export const dynamicParams = false
補足: インポートで
.mdx
ファイル拡張子を指定してください。これはモジュールパスエイリアス (例:@/content
) を使用するのに必須ではありませんが、インポートパスを簡素化します。
カスタムスタイルとコンポーネントの使用
マークダウンは、レンダリングされるとネイティブHTML要素にマッピングされます。たとえば、次のマークダウンを記述すると、
## This is a heading
This is a list in markdown:
- One
- Two
- Three
次のHTMLが生成されます
<h2>This is a heading</h2>
<p>This is a list in markdown:</p>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
マークダウンをスタイル設定するには、生成されたHTML要素にマッピングするカスタムコンポーネントを提供できます。スタイルとコンポーネントは、グローバル、ローカル、および共有レイアウトで実装できます。
グローバルスタイルとコンポーネント
mdx-components.tsx
にスタイルとコンポーネントを追加すると、アプリケーション内のすべてのMDXファイルに影響します。
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={{ color: 'red', fontSize: '48px' }}>{children}</h1>
),
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...(props as ImageProps)}
/>
),
...components,
}
}
ローカルスタイルとコンポーネント
ローカルスタイルとコンポーネントは、インポートされたMDXコンポーネントに渡すことで、特定のページに適用できます。これらは、グローバルスタイルとコンポーネントとマージされ、上書きします。
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} />
}
共有レイアウト
MDXページ間でレイアウトを共有するには、App Routerの組み込みレイアウトサポートを使用できます。
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Create any shared layout or styles here
return <div style={{ color: 'blue' }}>{children}</div>
}
Tailwind typographyプラグインの使用
Tailwindを使用してアプリケーションをスタイル設定している場合、@tailwindcss/typography
プラグインを使用すると、Tailwindの設定とスタイルをマークダウンファイルで再利用できます。
このプラグインは、マークダウンのようなソースからのコンテンツブロックにタイポグラフィスタイルを追加するために使用できる一連のprose
クラスを追加します。
Tailwind typographyをインストールし、共有レイアウトとともに使用して、必要なprose
を追加してください。
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Create any shared layout or styles here
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>
)
}
フロントマター
フロントマターは、ページに関するデータを保存するために使用できるYAMLのようなキー/値のペアリングです。@next/mdx
はデフォルトではフロントマターをサポートしていませんが、MDXコンテンツにフロントマターを追加するための多くのソリューションがあります。
@next/mdx
は、他のJavaScriptコンポーネントと同様にエクスポートを使用できます。
メタデータはMDXファイルの外部から参照できるようになりました。
import BlogPost, { metadata } from '@/content/blog-post.mdx'
export default function Page() {
console.log('metadata: ', metadata)
//=> { author: 'John Doe' }
return <BlogPost />
}
この一般的なユースケースは、MDXのコレクションを反復処理してデータを抽出する場合です。例えば、すべてのブログ投稿からブログインデックスページを作成する場合などです。Nodeのfs
モジュールやglobbyなどのパッケージを使用して、投稿のディレクトリを読み込み、メタデータを抽出できます。
補足:
fs
、globby
などは、サーバーサイドでのみ使用できます。- 完全な動作例については、ポートフォリオスターターキットテンプレートをご覧ください。
remarkとrehypeプラグイン
オプションで、MDXコンテンツを変換するためにremarkおよびrehypeプラグインを提供できます。
例えば、remark-gfm
を使用してGitHub Flavored Markdownをサポートできます。
remarkとrehypeのエコシステムはESMのみであるため、設定ファイルとしてnext.config.mjs
またはnext.config.ts
を使用する必要があります。
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Allow .mdx extensions for files
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
// Optionally, add any other Next.js config below
}
const withMDX = createMDX({
// Add markdown plugins here, as desired
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
})
// Combine MDX and Next.js config
export default withMDX(nextConfig)
Turbopackでのプラグインの使用
Turbopackでプラグインを使用するには、最新の@next/mdx
にアップグレードし、プラグイン名を文字列で指定します。
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)
補足:
シリアライズ可能なオプションを持たないremarkおよびrehypeプラグインは、JavaScript関数をRustに渡すことができないため、現時点ではTurbopackでは使用できません。
リモートMDX
MDXファイルやコンテンツが別の場所にある場合、サーバー上で動的に取得できます。これは、CMS、データベース、またはその他の場所に保存されているコンテンツに役立ちます。この用途で人気のあるコミュニティパッケージはnext-mdx-remote
です。
補足: 十分注意してください。MDXはJavaScriptにコンパイルされ、サーバーで実行されます。MDXコンテンツは信頼できるソースからのみ取得する必要があります。そうしないと、リモートコード実行 (RCE) につながる可能性があります。
以下の例ではnext-mdx-remote
を使用しています
import { MDXRemote } from 'next-mdx-remote/rsc'
export default async function RemoteMdxPage() {
// MDX text - can be from a database, CMS, fetch, anywhere...
const res = await fetch('https://...')
const markdown = await res.text()
return <MDXRemote source={markdown} />
}
/mdx-page-remote
ルートに移動すると、レンダリングされたMDXが表示されるはずです。
詳細解説: Markdownを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) // 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
のエコシステムには、シンタックスハイライト、見出しのリンク、目次の生成など、さまざまなプラグインが含まれています。
上記の例のように@next/mdx
を使用する場合、remark
やrehype
を直接使用する必要はありません。これらは自動的に処理されます。ここでは、@next/mdx
パッケージが内部で何をしているかについて深く理解するために説明しています。
RustベースのMDXコンパイラの使用 (実験的)
Next.jsは、Rustで記述された新しいMDXコンパイラをサポートしています。このコンパイラはまだ実験段階であり、本番環境での使用は推奨されません。新しいコンパイラを使用するには、withMDX
に渡す際にnext.config.js
を設定する必要があります。
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})
mdxRs
は、mdxファイルを変換する方法を設定するためのオブジェクトも受け入れます。
module.exports = withMDX({
experimental: {
mdxRs: {
jsxRuntime?: string // Custom jsx runtime
jsxImportSource?: string // Custom jsx import source,
mdxType?: 'gfm' | 'commonmark' // Configure what kind of mdx syntax will be used to parse & transform
},
},
})
便利なリンク
この情報は役立ちましたか?