MDX-блог на Next.js своими руками - как сделан этот блог
Как я собрал блог на Next.js 16 App Router, который работает на одних markdown-файлах, без CMS. Структура папок, настройка MDX и SEO на реальном коде.
Главное
Кладём статьи как .mdx файлы в src/content/posts и рендерим через @next/mdx - блог работает без CMS. Файл и есть URL, а вместо frontmatter список и метаданные строятся на meta export.
Содержание
У блога, который вы сейчас читаете, нет ни базы данных, ни админки. Каждая статья - это один 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
Чтобы использовать MDX в App Router, подключаем @next/mdx. В Next 16 по умолчанию работает Turbopack, поэтому плагины remark/rehype нужно передавать строковыми именами (ссылки на функции передать нельзя).
const withMDX = createMDX({
options: {
remarkPlugins: ["remark-gfm"], // 표·취소선 지원
rehypePlugins: ["rehype-slug"], // 제목 앵커
},
})
meta export вместо frontmatter
Обычно заголовок и дату пишут в блоке --- (frontmatter) в начале markdown-файла, но MDX умеет содержать JavaScript, так что есть способ почище. Просто экспортировать объект.
export const meta = {
title: "글 제목",
date: "2026-07-06",
category: "dev",
}
## 본문 시작
Страница списка динамически импортирует каждый файл и читает только этот 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 с первого коммита
С этой структурой вы поднимете свой блог за 30 минут. Я таким способом продолжаю копить истории о своих продуктах, например про Волну дня.
Частые вопросы
Почему MDX-файлы вместо CMS?
Статьи версионируются вместе с кодом, это бесплатно, а деплой равен публикации - для соло-разработчика это минимум трения. Плюс можно свободно вставлять картинки, код и компоненты.
Как построить список статей без frontmatter?
В начале каждого .mdx экспортируется объект meta, а страница списка динамически импортирует файлы и читает только этот meta. Отдельная библиотека парсинга не нужна.
Похожие статьи
- 💻 Разработка
5 приложений в одном репозитории - Turborepo-монорепа соло-разработчика
Как устроить Turborepo-монорепу для нескольких приложений и сайтов в одном репозитории. Общие конфиги, экономия сборок через кэш и деплой по приложениям - с практической точки зрения.
- 💻 Разработка
Self-hosting на мини-ПК с Docker и Cloudflare Tunnel
Как безопасно опубликовать несколько веб-сервисов с одного домашнего мини-ПК. Без открытия портов: HTTPS через Cloudflare Tunnel, запуск через Docker Compose.
- 📚 Учеба
Английские слова: почему их нужно не зубрить, а превращать в свои предложения
Почему заученные списки слов не помогают заговорить, и как превращать предметы вокруг себя в собственные предложения, которые остаются в памяти.