Markdown 和 MDX
Markdown 是一種輕量級標記語言,用於格式化文字。它讓您可以使用純文字語法編寫,並將其轉換為結構有效的 HTML。它通常用於在網站和部落格上編寫內容。
您編寫...
I **love** using [Next.js](https://nextjs.dev.org.tw/)
輸出
<p>I <strong>love</strong> using <a href="https://nextjs.dev.org.tw/">Next.js</a></p>
MDX 是 Markdown 的超集,可讓您在 Markdown 檔案中直接編寫 JSX。這是一種強大的方式,可在您的內容中新增動態互動性並嵌入 React 元件。
Next.js 可以支援應用程式內部的本地 MDX 內容,以及在伺服器上動態抓取的遠端 MDX 檔案。Next.js 外掛程式處理將 Markdown 和 React 元件轉換為 HTML,包括支援在伺服器元件中使用(App Router 中的預設值)。
要知道的好事:查看 Portfolio Starter Kit 範本,以取得完整可運作的範例。
安裝依賴套件
@next/mdx
套件和相關套件用於設定 Next.js,使其可以處理 Markdown 和 MDX。它從本地檔案取得資料,讓您可以直接在 /pages
或 /app
目錄中,使用 .md
或 .mdx
副檔名建立頁面。
安裝這些套件以使用 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
檔案在您的應用程式中充當頁面、路由或 import 語句。
新增 mdx-components.tsx
檔案
在專案的根目錄中建立一個 mdx-components.tsx
(或 .js
) 檔案,以定義全域 MDX 元件。例如,與 pages
或 app
相同的層級,或者在 src
內部(如果適用)。
import type { MDXComponents } from 'mdx/types'
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
}
}
要知道的好事:
- 使用 App Router 的
@next/mdx
需要mdx-components.tsx
,若沒有它將無法運作。- 深入瞭解
mdx-components.tsx
檔案慣例。- 瞭解如何使用自訂樣式和元件。
渲染 MDX
您可以使用 Next.js 的基於檔案的路由或通過將 MDX 檔案匯入其他頁面來渲染 MDX。
使用基於檔案的路由
當使用基於檔案的路由時,您可以使用 MDX 頁面,就像任何其他頁面一樣。
在 App Router 應用程式中,包括能夠使用 metadata。
在 /app
目錄中建立新的 MDX 頁面
my-project
├── app
│ └── mdx-page
│ └── page.(mdx/md)
|── mdx-components.(tsx/js)
└── package.json
您可以在這些檔案中使用 MDX,甚至可以直接在 MDX 頁面中 import 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 頁面。
使用 import 語句
在 /app
目錄中建立新頁面,並在您想要的任何位置建立 MDX 檔案
my-project
├── app
│ └── mdx-page
│ └── page.(tsx/js)
├── markdown
│ └── welcome.(mdx/md)
|── mdx-components.(tsx/js)
└── package.json
您可以在這些檔案中使用 MDX,甚至可以直接在 MDX 頁面中 import React 元件
在頁面內 import MDX 檔案以顯示內容
import Welcome from '@/markdown/welcome.mdx'
export default function Page() {
return <Welcome />
}
導航到 /mdx-page
路由應顯示您渲染的 MDX 頁面。
使用動態 import 語句
您可以 import 動態 MDX 元件,而不是使用檔案系統路由來處理 MDX 檔案。
例如,您可以有一個動態路由區段,從單獨的目錄載入 MDX 元件


generateStaticParams
可以用於預先渲染提供的路由。通過將 dynamicParams
標記為 false
,訪問未在 generateStaticParams
中定義的路由將會 404。
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const slug = (await params).slug
const { default: Post } = await import(`@/content/${slug}.mdx`)
return <Post />
}
export function generateStaticParams() {
return [{ slug: 'welcome' }, { slug: 'about' }]
}
export const dynamicParams = false
要知道的好事:確保在您的 import 語句中指定
.mdx
檔案副檔名。雖然不需要使用 模組路徑別名 (例如,@/content
),但它確實簡化了您的 import 路徑。
使用自訂樣式和元件
Markdown 在渲染時,會映射到原生 HTML 元素。例如,編寫以下 Markdown
## 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>
為了設定 Markdown 的樣式,您可以提供自訂元件,這些元件會映射到產生的 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,
}
}
本地樣式和元件
您可以通過將本地樣式和元件傳遞到 import 的 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
外掛程式 將允許您在 Markdown 檔案中重複使用 Tailwind 配置和樣式。
此外掛程式新增了一組 prose
類別,可用於將排版樣式新增至來自來源(如 Markdown)的內容區塊。
安裝 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>
)
}
Frontmatter
Frontmatter 是一種類似 YAML 的鍵/值配對,可用於儲存有關頁面的資料。@next/mdx
不預設支援 frontmatter,儘管有很多解決方案可將 frontmatter 新增到您的 MDX 內容,例如
@next/mdx
確實允許您像使用任何其他 JavaScript 元件一樣使用 exports
現在可以在 MDX 檔案外部參考 Metadata
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 之類的套件來讀取文章目錄並提取 metadata。
要知道的好事:
- 使用
fs
、globby
等只能在伺服器端使用。- 查看 Portfolio Starter Kit 範本,以取得完整可運作的範例。
remark 和 rehype 外掛程式
您可以選擇性地提供 remark 和 rehype 外掛程式來轉換 MDX 內容。
例如,您可以使用 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)
要知道的好事:
由於 無法將 JavaScript 函數傳遞給 Rust,因此尚無法將沒有可序列化選項的 remark 和 rehype 外掛程式與 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 無法原生理解 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) // 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
},
},
})
實用連結
這對您有幫助嗎?