这篇对比以前推荐的两个库,如今都是死路。Contentlayer 在 2023 年中失去维护者,从没支持过 React 19。而 next-mdx-remote 本身也在 2026 年 4 月 9 日被归档、变成只读——它自己的 README 现在都让你去别处。所以 2026 年真正要问的,已经不是「选 MDX bundler 还是 next-mdx-remote」,而是:在 Next.js 16 上、内容文件放在仓库里,哪条 MDX 路线还活着、够快,还能把大家怀念的 Contentlayer 那套类型安全 DX 补回来?
本文从决定一个内容站成败的几个点对比五个还在维护的方案:维护风险、构建时间、frontmatter 类型安全、产物体积。
一句话总结
- Next.js 16 上做新的文件系统内容站,多数情况下根本不需要任何 MDX 封装库。核心 MDX 库(
@mdx-js/mdx)在 React Server Component 里就能正常编译。这正是已归档的next-mdx-remoteREADME 现在推荐的做法。 - 想要一层带错误处理和 frontmatter 辅助函数的薄封装,就用
next-mdx-remote-client(v2.x,仍在维护,支持 React 19 和 RSC)——它是被归档那个包的直接继任者。 - 想把 Contentlayer「从 frontmatter 生成类型」的 DX 当成一个有人维护的依赖拿回来?用 Velite(构建时、Zod、框架无关)。注意:它的 webpack 插件在 Turbopack 下跑不起来。
- 只有当 MDX 来自 CMS/数据库、每篇文章 import 不同组件时,才用
mdx-bundler。对普通内容它的产物大约比 next-mdx-remote 大 4 倍,给仓库文件用属于杀鸡用牛刀。 - 只有在迁移已有 Contentlayer 项目、想要近乎无缝升级时,才考虑
contentlayer2(timlrx维护的 fork)。
各方案速览(截至 2026 年 6 月)
| 方案 | 状态 | 适合 | frontmatter 类型 | 主要代价 |
|---|---|---|---|---|
RSC 上的核心 @mdx-js/mdx | 活跃 | 新的文件系统站 | 自己写 Zod | 自己写约 80 行 |
next-mdx-remote-client v2.1.11 | 活跃(2026-05) | 薄封装 + 错误处理 | 自己写 Zod | 多一个依赖 |
| Velite | 活跃 | 想拿回 Contentlayer DX | 由 Zod 生成 | webpack 插件在 Turbopack 下挂 |
mdx-bundler | 活跃 | MDX 来自 CMS/数据库 | 自动解析 | 产物约大 4 倍 |
contentlayer2 / next-contentlayer2 | 社区 fork | 迁移老 Contentlayer 项目 | 自动生成 | 社区小,fork 有风险 |
next-mdx-remote | 2026-04-09 已归档 | 不再适合新项目 | — | 不再有修复 |
上表的版本和日期均为 2026 年 6 月的情况,动手前请到 npm 再确认一遍。
旧的默认选项为什么崩了
两个人气选手都是同样的死法:先是只有一个维护者,然后连维护者都没了。
- Contentlayer:最后一次有意义的发布在 2023 年中,那时 Next.js 14 都还没出。它能从 frontmatter 生成 TypeScript 类型、还跨构建缓存解析后的 MDX——DX 确实好用——但它从没跟上 App Router / React 19 这一代。新装就会撞上 peer-dependency 和构建失败。
- next-mdx-remote:HashiCorp 在 2026 年 4 月 9 日归档了仓库,最后一版是 v6.0.0(2026 年 2 月)。代码还能跑,但不会再有修复,README 明确建议改用
next-mdx-remote-client、mdx-bundler,或者干脆在 RSC 上用核心 MDX 库。
对一个想用好几年的内容站来说,教训很清楚:优先选依赖面最小的方案,这样一个仓库被归档也困不住你。
方案一:在 Server Component 里用核心 MDX(新的默认)
要干的事很小。一个 App Router 的 server component 读文件、跑官方 MDX 编译器、渲染结果。不需要任何第三方封装。
// app/articles/[slug]/page.tsx
import { compile, run } from '@mdx-js/mdx';
import * as runtime from 'react/jsx-runtime';
import matter from 'gray-matter';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { z } from 'zod';
import { mdxComponents } from '@/components/mdx';
const FrontmatterSchema = z.object({
title: z.string(),
description: z.string(),
publishedAt: z.coerce.date(),
tags: z.array(z.string()),
});
export default async function ArticlePage(
{ params }: { params: Promise<{ slug: string }> },
) {
const { slug } = await params;
const raw = await readFile(
path.join(process.cwd(), 'content', `${slug}.mdx`),
'utf8',
);
const { content, data } = matter(raw);
const meta = FrontmatterSchema.parse(data);
const compiled = await compile(content, { outputFormat: 'function-body' });
const { default: MDXContent } = await run(compiled, runtime);
return (
<article>
<h1>{meta.title}</h1>
<time>{meta.publishedAt.toISOString()}</time>
<MDXContent components={mdxComponents} />
</article>
);
}
generateStaticParams 读内容目录;generateMetadata 复用同一个 Zod schema。整条 pipeline 大约 80 行完全归你掌控的代码,零归档依赖。注意 Next.js 15 和 16 里 params 是个 Promise——要 await。
方案二:next-mdx-remote-client(薄封装)
如果你不想手接 compile/run,next-mdx-remote 仍在维护的继任者是 next-mdx-remote-client(截至 2026 年 5 月底为 v2.1.11;v2.x 面向 React 19)。它封装 @mdx-js/mdx,支持 MDX v3,把服务端(rsc)和客户端(csr)入口完全隔离,还加了一层内部错误处理和「不编译就读 frontmatter」的辅助函数。
// app/articles/[slug]/page.tsx
import { MDXRemote } from 'next-mdx-remote-client/rsc';
import { mdxComponents } from '@/components/mdx';
export default async function ArticlePage(/* ...读 raw、校验... */) {
return (
<MDXRemote
source={raw}
components={mdxComponents}
options={{ parseFrontmatter: true }}
/>
);
}
MDXRemote 是异步 server component,必须在服务端渲染。对多数文件系统站来说,这和方案一是同一件事,只是 API 更友好、自带错误状态。
方案三:内容在仓库外时用 mdx-bundler
mdx-bundler(Kent C. Dodds 出品)把 MDX 编译成自包含的 JS 字符串,运行时再 eval。它的优势出现在 MDX 来自数据库或 headless CMS、每篇文章 import 的组件各不相同时——bundler 把每次编译相互隔离。
// MDX 内容来自远端
import { bundleMDX } from 'mdx-bundler';
const { code, frontmatter } = await bundleMDX({
source: mdxStringFromCMS,
cwd: process.cwd(),
});
代价是:对普通内容,mdx-bundler 的产物大约是 next-mdx-remote 的 4 倍,因为它给每篇文章塞了一份 esbuild 打出来的 bundle。仓库里的 MDX 用不上这层隔离,所以请改用方案一或方案二。
把 Contentlayer 的 DX 补回来(用 Velite,或自己搭)
大家舍不得 Contentlayer 的三件事,2026 年每件都有干净的答案。
- Frontmatter 类型生成。 自己写:
z.infer<typeof FrontmatterSchema>,10 行 helper。或者交给 Velite 自动生成——它把你的content/文件夹从 Zod schema 变成一个带类型的数据层(JSON +.d.ts),框架无关。Velite 编译普通内容是毫秒级的,但它的VeliteWebpackPlugin在开启 Turbopack 时不工作——那种情况下把velite当成单独一步跑。 - 计算字段。 Contentlayer 从文件名推出
urlSlug。同样的模式:在lib/content.ts写个函数,把原始 frontmatter 映射成更丰富的对象。Velite 在 schema 里就直接支持计算字段。 - 跨页面缓存解析结果。 Contentlayer 在构建时复用解析后的 MDX。替代方案是按文件 mtime 做内存缓存,或者干脆不做——Next.js 16 的缓存(含 Cache Components)对大多数站已经够用。
自己手搭一份最小的 lib/content.ts 大概 150-200 行,DX 不输 Contentlayer,又不用扛一份没维护的依赖。Velite 让你少写些代码,代价是多一个(但仍活跃的)受管依赖。
容易踩的坑
- 服务端编译时 import 了仅限客户端的 MDX 组件,导致 hydration 不匹配。把交互组件放进
'use client'文件,再通过 components map 传进去。 - 忘了写
generateStaticParams。 每篇文章变动态路由,静态 SEO 和 Lighthouse 全掉;在 Next.js 16 上还会丢掉预渲染。 - 在 client component 里跑 MDX 编译。 RSC 的意义没了,运行时 bundle 还爆。
- 只在运行时校验 frontmatter。 schema 一出错就在生产挂。build 时也跑同一个 Zod 校验,最好接进 CI。
- 忘了
params现在是 Promise。 Next.js 15 起必须await params(和searchParams)——从老教程抄来的代码会直接报错。 - 残留没用的死依赖。 迁完执行
npm uninstall contentlayer next-contentlayer next-mdx-remote,清掉 peer-dep 报警、瘦一下 lockfile。
FAQ
- Contentlayer 是不是凉了?:原始仓库从 2023 年起就没维护、从没支持 React 19。社区有个 fork
contentlayer2/next-contentlayer2(timlrx维护),API 兼容,能在 Next.js 15、React 19 上跑。只在迁移已有 Contentlayer 项目时才用它,而且上车前先看仓库活跃度。 - 等等,next-mdx-remote 没了?:HashiCorp 在 2026 年 4 月 9 日归档了它。v6.0.0 还能装能跑,但不再有修复。新项目请用
next-mdx-remote-client,或直接用核心 MDX 库编译。 - next-mdx-remote 和 next-mdx-remote-client 有什么区别?:
next-mdx-remote-client(ipikuka出品)是仍在维护的 fork。它支持 MDX v3、React 19、完全分离的rsc/csr入口、内置错误处理,还有只读 frontmatter 的辅助函数。 - 直接用官方
@next/mdx行吗?:当每个 MDX 文件就是一个真正的路由页面(app/articles/foo/page.mdx)时行。一旦需要从内容目录做动态路由就崩——而多数内容站都需要。 - 这些都支持 remark / rehype 插件吗?:都支持。在 options 里传
remarkPlugins和rehypePlugins即可——核心 MDX、next-mdx-remote-client、mdx-bundler、Velite 都会转发它们。 - Astro 呢?:Astro 原生支持 MDX,content collections 和 Zod schema 内置——本站就是这么用的。想要 Contentlayer 的 DX 又不想踩 Contentlayer 的坑、又不需要 React 应用壳,Astro 是维护成本更低的那条路。