首页 / Next.js 16 入门教程 / MDX 与内容管理

Next.js 16 入门教程

MDX 与内容管理

本教程共 42 篇 · 第 35 篇 · 更新于 2026-07-30 · 约 7 分钟阅读

Next.jsNext.js 16 入门教程MDXMarkdown内容管理CMS代码高亮

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)

注意remarkrehype 插件生态是 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-frontmattergray-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)

内容管理最佳实践

  1. 内容分离:把 MDX 文件放在 content/ 目录,和代码分开管理
  2. 类型安全:给 frontmatter 定义 TypeScript 类型
  3. 图片优化:用 next/image 替换默认的 img
  4. 目录生成:用 remark-toc 自动生成文章目录
  5. 阅读时间:计算并显示预估阅读时间

MDX 是构建内容型网站的利器。博客、文档、教程站点都适合用它来实现。