Для интеграции MDX в проект на Next.js необходимо установить
несколько пакетов: @next/mdx и @mdx-js/loader
(или их современные аналоги для Next.js 13+). Используется конфигурация
через next.config.js с применением функции
withMDX. Это позволяет импортировать файлы с расширением
.mdx как компоненты React.
Пример конфигурации:
const withMDX = require('@next/mdx')({
extension: /\.mdx?$/
});
module.exports = withMDX({
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx']
});
Важный момент: расширение файлов должно включать mdx,
иначе Next.js не будет их обрабатывать как страницы.
MDX-файл сочетает в себе Markdown и JSX. Стандартный шаблон выглядит так:
---
title: "Пример MDX"
date: "2026-03-23"
---
# Заголовок
Это обычный текст в Markdown.
<MyComponent prop="value" />
Раздел --- в начале файла называется
frontmatter и хранит метаданные, которые можно использовать в
Next.js для генерации статических страниц через
getStaticProps или getStaticPaths.
В MDX можно импортировать любые React-компоненты, что позволяет создавать интерактивные статьи, документацию или блог. Пример:
import Alert from '../components/Alert';
<Alert type="warning">
Это предупреждающее сообщение.
</Alert>
Компоненты могут быть встроенными (как Alert) или
глобальными, если настроить MDXProvider для всего
приложения:
import { MDXProvider } from '@mdx-js/react';
import CustomH1 from '../components/CustomH1';
const components = {
h1: CustomH1
};
export default function App({ Component, pageProps }) {
return (
<MDXProvider components={components}>
<Component {...pageProps} />
</MDXProvider>
);
}
Таким образом, все заголовки h1 в MDX-файлах будут
рендериться через компонент CustomH1.
Next.js позволяет использовать MDX для статических страниц. Основные функции:
getStaticProps для передачи контента страницы;getStaticPaths для динамических маршрутов.Пример генерации страницы блога:
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { serialize } from 'next-mdx-remote/serialize';
import { MDXRemote } from 'next-mdx-remote';
const postsDirectory = path.join(process.cwd(), 'posts');
export async function getStaticPaths() {
const filenames = fs.readdirSync(postsDirectory);
const paths = filenames.map((name) => ({
params: { slug: name.replace(/\.mdx$/, '') }
}));
return { paths, fallback: false };
}
export async function getStaticProps({ params }) {
const filePath = path.join(postsDirectory, `${params.slug}.mdx`);
const source = fs.readFileSync(filePath, 'utf8');
const { content, data } = matter(source);
const mdxSource = await serialize(content);
return { props: { mdxSource, frontMatter: data } };
}
export default function Post({ mdxSource, frontMatter }) {
return (
<>
<h1>{frontMatter.title}</h1>
<MDXRemote {...mdxSource} />
</>
);
}
Здесь gray-matter извлекает frontmatter, а
next-mdx-remote позволяет рендерить MDX с переданными
компонентами и данными.
MDX поддерживает плагины для расширенной обработки Markdown и HTML. В
serialize можно передать опции:
import { serialize } from 'next-mdx-remote/serialize';
import remarkGfm from 'remark-gfm';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
const mdxSource = await serialize(content, {
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings]
}
});
remark-gfm добавляет поддержку таблиц, чекбоксов и
других расширений GitHub Flavored Markdown.rehype-slug генерирует уникальные ID для
заголовков.rehype-autolink-headings добавляет ссылки на заголовки
для быстрой навигации.MDX позволяет встроить React-компоненты с состоянием и динамикой прямо в текст. Пример счетчика:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onCl ick={() => setCount(count + 1)}>
Нажато {count} раз
</button>
);
}
<Counter />
Такой подход делает MDX идеальным инструментом для документации библиотек с живыми демо-примерами.
Рекомендуется хранить все MDX-файлы в отдельной директории, например
posts или docs. Для удобства можно создать
утилиты для загрузки и сортировки файлов по дате или категории. Пример
функции сортировки постов:
export function getSortedPosts() {
const files = fs.readdirSync(postsDirectory);
const posts = files.map((file) => {
const source = fs.readFileSync(path.join(postsDirectory, file), 'utf8');
const { data } = matter(source);
return { slug: file.replace(/\.mdx$/, ''), ...data };
});
return posts.sort((a, b) => new Date(b.date) - new Date(a.date));
}
Это позволяет автоматически формировать списки статей и навигацию по сайту.
Для больших проектов рекомендуется:
next-mdx-remote для динамической
сериализации MDX, что уменьшает размер бандла.MDX полностью совместим с TypeScript. Можно создавать компоненты с типами и импортировать их в MDX-файлы:
type AlertProps = {
type: 'info' | 'warning';
children: React.ReactNode;
};
const Alert: React.FC<AlertProps> = ({ type, children }) => (
<div className={`alert ${type}`}>{children}</div>
);
export default Alert;
MDX корректно подхватывает типы при импорте и обеспечивает автодополнение в редакторах.
MDX в связке с Next.js объединяет статический и динамический контент, позволяет использовать React-компоненты прямо в Markdown, поддерживает frontmatter для метаданных и легко интегрируется с Remark и Rehype. Это делает его идеальным инструментом для создания документации, блогов, интерактивных руководств и образовательных платформ.