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 に必要なパッケージをインストールします:
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
next.config.js
ファイルをあなたのプロジェクトの root に更新して、それを MDX を使用するように設定します:
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)
その後、/pages
ディレクトリ内に新しい MDX ページを作成します:
your-project
├── pages
│ └── my-mdx-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
を使用します:
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 text - can be from a local file, database, CMS, fetch, anywhere...
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 text - can be from a local file, database, CMS, fetch, anywhere...
const res = await fetch('https:...')
const mdxText = await res.text()
const mdxSource = await serialize(mdxText)
return { props: { mdxSource } }
}
/my-mdx-page-remote
への route に移動すると、rendering された MDX が表示されるはずです。
Layouts
MDX ページで layout を共有するには、layout component を作成します:
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Create any shared layout or styles here
return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MdxLayout({ children }) {
// Create any shared layout or styles here
return <div style={{ color: 'blue' }}>{children}</div>
}
それから、MDX ページに layout component を import し、MDX の内容を layout でラップし、それを export してください:
import MdxLayout from "../components/mdx-layout";
# Welcome to my MDX page!
export default function MDXPage({ children }) {
return <MdxLayout>{children}</MdxLayout>;
}
Remark and Rehype Plugins
必要に応じて、MDX コンテンツを transform するためのremark
およびrehype
プラグインを提供することができます。
たとえば、GitHub フレーバーのマークダウンをサポートするためにremark-gfm
を使用することができます。
remark
とrehype
エコシステムが ESM のみなので、設定ファイルとして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(pages/
またはsrc/
の親フォルダ)に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,
}
}
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 に変換する必要があります。これはremark
とrehype
を使用して達成することができます。
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
を使用する際、remark
やrehype
を直接使用する必要はありません。なぜなら、それらはあなたのために処理されているからです。ここでは、@next/mdx
パッケージが裏側で何を行っているのかをより深く理解するために説明しています。
Using the Rust-based MDX compiler (Experimental)
Next.js は、Rust で書かれた新しい MDX compiler をサポートしています。この compiler はまだ実験段階であり、production での使用は推奨されていません。新しい compiler を使用するには、 withMDX
に渡すときに next.config.js
を設定する必要があります。
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})