用 Next.js 从零搭一个 MDX 博客 - 这个博客就是这么做的
不用 CMS,只靠 Markdown 文件就能运转的博客,用 Next.js 16 App Router 搭建的全过程。目录结构、MDX 配置、SEO,全部按实际代码流程整理。
核心摘要
把文章以 .mdx 文件放在 src/content/posts 下,用 @next/mdx 渲染,不需要 CMS 博客也能跑起来。文件即 URL,用 meta export 代替 frontmatter 来生成列表和元数据。
目录
你现在读的这个博客,既没有数据库,也没有后台管理界面。每篇文章就是一个 Markdown 文件。怎么做的,我原样公开。
为什么选文件方案
博客工具很多,但对独立开发者来说,大部分都太重了。
- CMS: 附带管理界面、对接工作和费用
- Notion 集成: 方便,但在渲染、速度、自定义上会卡壳
- Markdown 文件: 文章和代码一起进版本管理,免费,
git push就是发布
我选了第三种。因为摩擦最小。
目录结构
只看核心的话,长这样。
src/
├── app/
│ ├── page.tsx # 홈 - 글 목록
│ ├── posts/[slug]/page.tsx # 글 상세
│ └── sitemap.ts # /sitemap.xml
├── content/posts/*.mdx # ← 글은 여기에만 추가
└── lib/posts.ts # 목록·메타 로딩
文件名直接就是 URL。hello.mdx 会以 /posts/hello 打开。
MDX 配置
在 App Router 里用 MDX,要接上 @next/mdx。Next 16 默认使用 Turbopack,所以 remark/rehype 插件必须以字符串名称传入(不能传函数引用)。
const withMDX = createMDX({
options: {
remarkPlugins: ["remark-gfm"], // 표·취소선 지원
rehypePlugins: ["rehype-slug"], // 제목 앵커
},
})
用 meta export 代替 frontmatter
通常我们在 Markdown 顶部的 --- 块(frontmatter)里写标题和日期,但 MDX 能容纳 JavaScript,所以有更干净的做法。直接 export 一个对象就行。
export const meta = {
title: "글 제목",
date: "2026-07-06",
category: "dev",
}
## 본문 시작
列表页把每个文件动态 import 进来,只读这个 meta。
const mod = await import(`@/content/posts/${slug}.mdx`)
return mod.meta // 본문 렌더 없이 메타만
不需要解析库,也不需要单独的索引。
SEO 从第一天就要有
如果博客的目的是获取流量,SEO 就不该留到以后,而要从一开始就做。我给每篇文章配了元数据、Open Graph、JSON-LD 结构化数据,再加上 sitemap.xml、robots.txt 和 rss.xml,全部作为默认配置铺好了。这部分下一篇文章再细讲。
小结
- 文章就是一个
.mdx文件,文件名即 URL @next/mdx+ 字符串形式的插件(Turbopack)- 用
export const meta代替 frontmatter - SEO 从第一个 commit 就开始
照着这个结构,你的博客 30 分钟也能上线。我正是用这种方式,持续记录着 今日海浪 这类产品背后的故事。
常见问题
为什么用 MDX 文件而不是 CMS?
文章和代码一起进版本管理,完全免费,部署即发布,对独立开发者来说摩擦最小。图片、代码、组件也都能自由嵌入。
没有 frontmatter,怎么生成文章列表?
在每个 .mdx 顶部 export 一个 meta 对象,列表页动态 import 文件、只读取这个 meta 即可。不需要额外的解析库。
相关文章
- 💻 开发
一个桶有两个主人时,最后 apply 的一方获胜
我加了一条 S3 lifecycle 规则,terraform apply 却失败了。原因是同一个桶的 lifecycle 配置被两个资源各自拥有。S3 lifecycle 不是按规则粒度,而是按整个文档粒度生效,最后的赢家会悄悄抹掉对方的规则。
- 💻 开发
测试一片绿,通知却从来没发出去过一次
往队列里塞任务的代码从上线第一天起就每次都失败。错误被吞掉了,测试靠 mock 一直是绿的。一个冒号悄悄干掉三个功能的故事。
- 💻 开发
收到了数据库挂了的告警。可数据库从来没挂过
一天之内堆了四条 CRITICAL 告警。504、Prisma P2028、后台 500,还有一条“DATABASE 服务宕机”。我先打开 RDS 指标,结果那 21 小时里 CPU 峰值只有 19.7%,好得很。慢的不是数据库,而是要横跨大西洋二十五次的往返;而那条“宕机”告警,是健康检查自己造出来的。