MDX
Markdown — это облегчённый язык разметки для форматирования текста. Он позволяет писать с использованием простого текстового синтаксиса и преобразовывать его в валидный HTML. Часто используется для написания контента на сайтах и в блогах.
Вы пишете...
I **love** using [Next.js](https://nextjs.org/)
Результат:
<p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>
MDX — это расширение Markdown, позволяющее использовать JSX непосредственно в файлах Markdown. Это мощный способ добавить динамическую интерактивность и встраивать React-компоненты в ваш контент.
Next.js поддерживает как локальный MDX-контент внутри приложения, так и удалённые MDX-файлы, загружаемые динамически на сервере. Плагин Next.js преобразует Markdown и React-компоненты в HTML, включая поддержку использования в Server Components (по умолчанию в App Router).
@next/mdx
Пакет @next/mdx
используется для настройки Next.js для обработки Markdown и MDX. Он работает с локальными файлами, позволяя создавать страницы с расширением .mdx
прямо в директориях /pages
или /app
.
Рассмотрим, как настроить и использовать MDX в Next.js.
Начало работы
Установите необходимые пакеты для рендеринга MDX:
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
Обновите файл next.config.js
в корне проекта для настройки MDX:
const withMDX = require('@next/mdx')()
/** @type {import('next').NextConfig} */
const nextConfig = {
// Настройка `pageExtensions` для включения MDX-файлов
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
// Опционально: добавьте другие настройки Next.js
}
module.exports = withMDX(nextConfig)
Затем создайте новую MDX-страницу в директории /pages
:
ваш-проект
├── pages
│ └── my-mdx-page.mdx
└── package.json
Теперь вы можете использовать Markdown и импортировать React-компоненты прямо в MDX-странице:
import { MyComponent } from 'my-components'
# Добро пожаловать на мою MDX-страницу!
Это **жирный** и _курсивный_ текст.
Список в Markdown:
- Один
- Два
- Три
Посмотрите мой React-компонент:
<MyComponent />
Переход по маршруту /my-mdx-page
должен отобразить ваш MDX-контент.
Удалённый MDX
Если ваши Markdown или MDX-файлы находятся где-то ещё, вы можете загружать их динамически на сервере. Это полезно для контента, хранящегося в отдельной локальной папке, CMS, базе данных или другом месте.
Популярные пакеты для загрузки MDX-контента:
Полезно знать: Будьте осторожны. MDX компилируется в JavaScript и выполняется на сервере. Загружайте MDX-контент только из доверенных источников, иначе это может привести к удалённому выполнению кода (RCE).
Следующий пример использует next-mdx-remote
:
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'
interface Props {
mdxSource: MDXRemoteSerializeResult
}
export default function RemoteMdxPage({ mdxSource }: Props) {
return <MDXRemote {...mdxSource} />
}
export async function getStaticProps() {
// MDX-текст — может быть из локального файла, базы данных, CMS, fetch и т.д.
const res = await fetch('https:...')
const mdxText = await res.text()
const mdxSource = await serialize(mdxText)
return { props: { source: mdxSource } }
}
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'
export default function RemoteMdxPage({ mdxSource }) {
return <MDXRemote {...mdxSource} />
}
export async function getStaticProps() {
// MDX-текст — может быть из локального файла, базы данных, CMS, fetch и т.д.
const res = await fetch('https:...')
const mdxText = await res.text()
const mdxSource = await serialize(mdxText)
return { props: { source: mdxSource } }
}
Переход по маршруту /my-mdx-page-remote
должен отобразить ваш MDX-контент.
Макеты
Для общего макета MDX-страниц создайте компонент макета:
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// Создайте общий макет или стили здесь
return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MdxLayout({ children }) {
// Создайте общий макет или стили здесь
return <div style={{ color: 'blue' }}>{children}</div>
}
Затем импортируйте компонент макета в MDX-страницу, оберните MDX-контент в макет и экспортируйте его:
import MdxLayout from '../components/mdx-layout'
# Добро пожаловать на мою MDX-страницу!
export default function MDXPage({ children }) {
return <MdxLayout>{children}</MdxLayout>;
}
Плагины Remark и Rehype
Вы можете опционально использовать плагины remark
и rehype
для преобразования MDX-контента.
Например, можно использовать remark-gfm
для поддержки GitHub Flavored Markdown.
Поскольку экосистема remark
и rehype
работает только с ESM, вам нужно использовать файл конфигурации next.config.mjs
.
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Настройка `pageExtensions` для включения MDX-файлов
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
// Опционально: добавьте другие настройки Next.js
}
const withMDX = createMDX({
// Добавьте плагины Markdown по желанию
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
})
// Объедините настройки MDX с настройками Next.js
export default withMDX(nextConfig)
Фронтматтер (Frontmatter)
Фронтматтер — это YAML-подобные пары ключ/значение для хранения данных о странице. @next/mdx
не поддерживает фронтматтер по умолчанию, но есть решения для его добавления, например:
Для доступа к метаданным страницы с @next/mdx
можно экспортировать объект meta
из .mdx
-файла:
export const meta = {
author: 'Иван Иванов',
}
# Моя MDX-страница
Пользовательские элементы
Одно из преимуществ Markdown — соответствие нативным HTML
-элементам, что делает написание быстрым и интуитивным:
Это список в Markdown:
- Один
- Два
- Три
Это генерирует следующий HTML
:
<p>Это список в Markdown:</p>
<ul>
<li>Один</li>
<li>Два</li>
<li>Три</li>
</ul>
Для стилизации собственных элементов можно использовать шорткоды — пользовательские компоненты, соответствующие HTML
-элементам.
Для этого создайте файл mdx-components.tsx
в корне приложения (родительская папка для pages/
или src/
) и добавьте пользовательские элементы:
import type { MDXComponents } from 'mdx/types'
import Image from 'next/image'
// Этот файл позволяет предоставлять пользовательские React-компоненты
// для использования в MDX-файлах. Можно импортировать любые
// React-компоненты, включая inline-стили,
// компоненты из других библиотек и т.д.
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
// Позволяет кастомизировать встроенные компоненты, например, для добавления стилей.
h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...props}
/>
),
...components,
}
}
import Image from 'next/image'
// Этот файл позволяет предоставлять пользовательские React-компоненты
// для использования в MDX-файлах. Можно импортировать любые
// React-компоненты, включая inline-стили,
// компоненты из других библиотек и т.д.
export function useMDXComponents(components) {
return {
// Позволяет кастомизировать встроенные компоненты, например, для добавления стилей.
h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...props}
/>
),
...components,
}
}
Подробнее: как Markdown преобразуется в HTML?
React не понимает Markdown "из коробки". Текст Markdown сначала нужно преобразовать в HTML. Это можно сделать с помощью remark
и rehype
.
remark
— это экосистема инструментов для работы с Markdown. rehype
— аналогично, но для HTML. Например, следующий код преобразует Markdown в HTML:
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
main()
async function main() {
const file = await unified()
.use(remarkParse) // Преобразование в AST Markdown
.use(remarkRehype) // Преобразование в AST HTML
.use(rehypeSanitize) // Санитизация HTML
.use(rehypeStringify) // Преобразование AST в HTML
.process('Hello, Next.js!')
console.log(String(file)) // <p>Hello, Next.js!</p>
}
Экосистема remark
и rehype
включает плагины для подсветки синтаксиса, автоматических ссылок на заголовки, генерации оглавления и другого.
При использовании @next/mdx
вам не нужно работать с remark
или rehype
напрямую — они обрабатываются автоматически. Мы описываем это для более глубокого понимания работы @next/mdx
.
Использование MDX-компилятора на Rust (экспериментально)
Next.js поддерживает новый MDX-компилятор на Rust. Он пока экспериментальный и не рекомендуется для продакшена. Для его использования настройте next.config.js
при передаче в withMDX
:
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})