Why NextWhy Next
К списку
💻 Разработка3 мин чтения

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. Отдельная библиотека парсинга не нужна.

Похожие статьи