MDX — это расширение Markdown, позволяющее вставлять JSX-компоненты прямо в текст. В связке с Remix это открывает возможности создания интерактивной документации, блогов и образовательного контента с компонентами React. Для корректной работы MDX в Remix необходимо понимать несколько ключевых аспектов: конфигурацию сборки, маршрутизацию, обработку данных и рендеринг компонентов.
Для начала нужно установить необходимые пакеты:
npm install @mdx-js/react @mdx-js/loader
Remix изначально использует Vite или Webpack (в зависимости от версии проекта). Для Vite необходимо настроить MDX как плагин:
import mdx from '@mdx-js/rollup';
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react(),
mdx()
]
});
Для Webpack используется @mdx-js/loader:
module.exports = {
module: {
rules: [
{
test: /\.mdx$/,
use: [
'babel-loader',
'@mdx-js/loader'
]
}
]
}
};
Это обеспечивает корректное преобразование .mdx файлов в
React-компоненты, которые можно импортировать в маршруты Remix.
Обычно MDX-файлы размещают в папке app/routes/content
или аналогичной. Пример структуры:
app/
├─ routes/
│ ├─ blog/
│ │ ├─ index.tsx
│ │ └─ post-1.mdx
└─ components/
└─ Highlight.tsx
В файле маршрута можно импортировать MDX-компонент напрямую:
import Post1 from './post-1.mdx';
export default function BlogPost() {
return (
<article>
<Post1 components={{ Highlight }} />
</article>
);
}
MDX позволяет заменять стандартные HTML-теги на React-компоненты. Это
делается через объект components, передаваемый в
MDX-компонент:
const components = {
h1: (props) => <h1 style={{ color: 'tomato' }} {...props} />,
pre: (props) => <pre className="custom-pre" {...props} />,
Highlight
};
<Post1 components={components} />
В этом примере все заголовки <h1> будут окрашены в
томато, а блоки кода обернуты в кастомный компонент
Highlight. Такой подход позволяет единообразно стилизовать
контент и внедрять интерактивные элементы.
Remix предлагает функцию loader для предзагрузки данных.
В сочетании с MDX можно динамически подгружать контент:
import fs from 'fs';
import path from 'path';
import { json } from '@remix-run/node';
import { MDXContent } from '@mdx-js/react';
export const loader = async () => {
const filePath = path.join(process.cwd(), 'app/routes/blog/post-1.mdx');
const source = fs.readFileSync(filePath, 'utf-8');
return json({ source });
};
export default function BlogPost({ data }) {
return (
<article>
<MDXContent components={{ Highlight }} children={data.source} />
</article>
);
}
Такой подход позволяет разделять контент и логику рендеринга, облегчая масштабирование сайта и добавление новых статей.
MDX открывает возможности для интеграции сложных React-компонентов:
function Counter() {
const [count, setCount] = React.useState(0);
return (
<button onCl ick={() => setCount(count + 1)}>
Нажато {count} раз
</button>
);
}
В MDX-файле можно использовать компонент прямо в тексте:
# Интерактивный счетчик
<Counter />
Это делает контент живым и динамичным, что особенно полезно для образовательных и технических блогов.
Для создания блога с динамическими страницами можно использовать параметризованные маршруты Remix:
app/routes/blog/$slug.tsx
Внутри маршрута читается соответствующий MDX-файл на основе параметра
slug:
import fs from 'fs';
import path from 'path';
import { useParams } from '@remix-run/react';
import { MDXRemote } from 'next-mdx-remote';
export default function Post() {
const { slug } = useParams();
const filePath = path.join(process.cwd(), `app/routes/blog/${slug}.mdx`);
const source = fs.readFileSync(filePath, 'utf-8');
return <MDXRemote {...source} components={{ Highlight }} />;
}
Это позволяет создавать блоги, документацию и другие проекты, где новые страницы добавляются простым добавлением MDX-файла без изменения маршрутов.
Для крупных проектов рекомендуется:
remark и rehype плагины для
автоматической обработки MDX (например, синтаксическая подсветка,
таблицы содержимого).Такой подход обеспечивает структурированность, масштабируемость и легкость поддержки проектов на Remix с MDX.