MDX 与内容管理
本教程共 42 篇 · 第 35 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
35. MDX 与内容管理
本节目标:掌握在 Next.js 中使用 MDX 的方法,包括配置、渲染、自定义组件和动态内容加载。
MDX 是什么
Markdown 是一种轻量标记语言,用纯文本就能写出格式化的内容。MDX 是 Markdown 的超集,允许在 Markdown 中直接写 JSX 和导入 React 组件。
# 欢迎使用 MDX
这是一段 **粗体** 文本。
import { Chart } from '@/components/Chart'
<Chart data={data} />
Next.js 通过 @next/mdx 插件支持 MDX,可以在应用中创建 .mdx 文件作为页面或内容。
安装配置
安装依赖
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
配置 next.config.mjs
import createMDX from '@next/mdx'
const nextConfig = {
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
}
const withMDX = createMDX({
// 插件配置
})
export default withMDX(nextConfig)
注意:
remark和rehype插件生态是 ESM only,所以配置文件要用.mjs或.ts扩展名。
创建 mdx-components.tsx
这个文件是必需的,定义全局可用的 MDX 组件:
import type { MDXComponents } from 'mdx/types'
const components: MDXComponents = {}
export function useMDXComponents(): MDXComponents {
return components
}
渲染 MDX
方式一:文件路由
直接把 .mdx 文件当作页面:
app/
├── mdx-page/
│ └── page.mdx
# 我的 MDX 页面
这是用 MDX 写的页面内容。
访问 /mdx-page 就能看到渲染后的内容。
方式二:导入 MDX
把 MDX 文件当作组件导入:
// content/welcome.mdx
import { MyComponent } from 'my-component'
# 欢迎
<MyComponent />
// app/page.tsx
import Welcome from '@/content/welcome.mdx'
export default function Page() {
return <Welcome />
}
方式三:动态导入
适合博客这种根据 slug 加载不同文章的场景:
// app/blog/[slug]/page.tsx
export default async function Page({ params }) {
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
自定义组件
Markdown 渲染的 HTML 元素可以替换成自定义组件。
全局组件
在 mdx-components.tsx 中定义,影响所有 MDX 文件:
import type { MDXComponents } from 'mdx/types'
import Image, { ImageProps } from 'next/image'
const components = {
h1: ({ children }) => (
<h1 style={{ color: 'red', fontSize: '48px' }}>{children}</h1>
),
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...(props as ImageProps)}
/>
),
} satisfies MDXComponents
export function useMDXComponents(): MDXComponents {
return components
}
局部组件
通过 components 属性传入,只影响当前组件:
import Welcome from '@/content/welcome.mdx'
function CustomH1({ children }) {
return <h1 style={{ color: 'blue' }}>{children}</h1>
}
export default function Page() {
return <Welcome components={{ h1: CustomH1 }} />
}
局部组件会覆盖全局组件。
共享布局
用 App Router 的 layout 给所有 MDX 页面套上统一样式:
// app/mdx-page/layout.tsx
export default function MdxLayout({ children }) {
return (
<div className="prose prose-headings:mt-8 prose-headings:font-semibold">
{children}
</div>
)
}
如果用 Tailwind,可以用 @tailwindcss/typography 插件的 prose 类。
Frontmatter 和元数据
@next/mdx 默认不支持 YAML frontmatter,但可以用 export 导出元数据:
export const metadata = {
author: '张三',
date: '2026-07-30',
}
# 文章标题
在外部引用:
import BlogPost, { metadata } from '@/content/blog-post.mdx'
export default function Page() {
console.log(metadata.author) // '张三'
return <BlogPost />
}
如果需要 YAML frontmatter,可以安装 remark-frontmatter 或 gray-matter。
插件系统
remark 和 rehype
- remark:处理 Markdown AST
- rehype:处理 HTML AST
常用插件:
import remarkGfm from 'remark-gfm'
const withMDX = createMDX({
options: {
remarkPlugins: [remarkGfm], // 支持 GitHub 风格 Markdown
rehypePlugins: [],
},
})
Turbopack 下的插件配置
Turbopack 下插件名要用字符串格式(因为函数不能从 JS 传给 Rust):
const withMDX = createMDX({
options: {
remarkPlugins: [
'remark-gfm',
['remark-toc', { heading: '目录' }],
],
rehypePlugins: [
'rehype-slug',
['rehype-katex', { strict: true }],
],
},
})
代码高亮
MDX 中的代码块可以用 rehype-pretty-code 做语法高亮:
import rehypePrettyCode from 'rehype-pretty-code'
const withMDX = createMDX({
options: {
rehypePlugins: [
[rehypePrettyCode, { theme: 'github-dark' }],
],
},
})
Rust 编译器(实验性)
Next.js 支持用 Rust 编写的 MDX 编译器,速度更快但还在实验阶段:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
mdxRs: true,
},
}
export default withMDX(nextConfig)
内容管理最佳实践
- 内容分离:把 MDX 文件放在
content/目录,和代码分开管理 - 类型安全:给 frontmatter 定义 TypeScript 类型
- 图片优化:用
next/image替换默认的img - 目录生成:用
remark-toc自动生成文章目录 - 阅读时间:计算并显示预估阅读时间
MDX 是构建内容型网站的利器。博客、文档、教程站点都适合用它来实现。