Server-side rendering

Server-side rendering (SSR) позволяет генерировать HTML на сервере до того, как страница будет отправлена клиенту. В контексте MDX это особенно полезно, так как позволяет сразу отдавать готовый контент с JSX-компонентами, встроенными прямо в текст, без необходимости ждать выполнения на стороне клиента.

MDX — это расширение Markdown, которое позволяет использовать JSX внутри Markdown-документов. Это открывает возможности динамического контента в статичных статьях и документации, сохраняя при этом читаемость исходного Markdown.


Подключение MDX к SSR

Для работы с MDX на сервере чаще всего используют такие инструменты, как @mdx-js/mdx и @mdx-js/react. Основной процесс включает три шага:

  1. Компиляция MDX в JSX MDX-файлы сначала преобразуются в JSX-код с помощью функции compile или compileSync из @mdx-js/mdx. Например:

    import { compile } from '@mdx-js/mdx';
    import fs from 'fs';
    
    const source = fs.readFileSync('example.mdx', 'utf8');
    const compiled = await compile(source, { outputFormat: 'function-body' });

    Здесь ключевой момент — outputFormat: 'function-body', который возвращает функцию для рендеринга контента на сервере.

  2. Создание компонента для рендеринга После компиляции необходимо превратить скомпилированный код в React-компонент. Для этого используют @mdx-js/react и функцию MDXContent:

    import { MDXProvider } from '@mdx-js/react';
    import * as runtime from 'react/jsx-runtime';
    
    const MDXComponent = new Function('React', `${compiled.value}`)(runtime);

    MDXProvider позволяет обернуть MDX-контент, предоставляя кастомные компоненты для рендеринга заголовков, ссылок, изображений и других элементов.

  3. Рендеринг на сервере Для SSR используют react-dom/server:

    import ReactDOMServer from 'react-dom/server';
    
    const html = ReactDOMServer.renderToString(
      <MDXProvider components={customComponents}>
        <MDXComponent />
      </MDXProvider>
    );

    Полученный HTML можно сразу вставлять в шаблон страницы или отдавать как ответ на HTTP-запрос.


Оптимизация SSR с MDX

  • Кэширование скомпилированного MDX Компиляция MDX в JSX — процесс затратный по времени. Для ускорения серверной генерации рекомендуется сохранять скомпилированные файлы в кэш или использовать систему сборки на этапе деплоя.

  • Разделение компонентов и контента В MDX можно выносить часто используемые элементы в отдельные React-компоненты. Это снижает дублирование кода и ускоряет SSR, так как повторно используемые компоненты уже загружены в память сервера.

  • Lazy-loading для тяжелых компонентов Если MDX содержит интерактивные элементы, их рендеринг на клиенте можно делать через динамический импорт. На сервере оставляется базовый HTML, а интерактивность подключается позже.


Примеры интеграции с фреймворками

  • Next.js Next.js имеет встроенную поддержку SSR. MDX интегрируется через @next/mdx или next-mdx-remote. Пример:

    import { serialize } from 'next-mdx-remote/serialize';
    import { MDXRemote } from 'next-mdx-remote';
    
    export async function getServerSideProps() {
      const source = fs.readFileSync('example.mdx', 'utf8');
      const mdxSource = await serialize(source);
      return { props: { mdxSource } };
    }
    
    export default function Page({ mdxSource }) {
      return <MDXRemote {...mdxSource} components={customComponents} />;
    }

    Здесь MDX компилируется на сервере и передается в компонент MDXRemote для рендеринга на клиенте.

  • Express + React Можно использовать Express для отдачи HTML, сгенерированного с помощью react-dom/server. Важное отличие от Next.js — ручное управление компиляцией и кэшированием MDX.


Подводные камни SSR с MDX

  • Состояние компонентов React-компоненты в MDX могут иметь состояние. При SSR состояние создается на сервере, но на клиенте оно пересоздается заново, что может вызвать рассинхронизацию. Использование useEffect для инициализации состояния после монтирования решает проблему.

  • Глобальные стили Стили компонентов, встроенных в MDX, должны быть корректно подключены на сервере. Иначе на клиенте будет «мигание» контента без стилей.

  • Асинхронные операции SSR не поддерживает асинхронные хуки напрямую в компоненте MDX. Необходимо предварительно получать данные и передавать их через props.


Рекомендации по структуре MDX для SSR

  1. Минимизировать сложную логику внутри MDX-файлов.
  2. Использовать отдельные React-компоненты для интерактивного контента.
  3. Оборачивать MDX через MDXProvider для унификации отображения элементов.
  4. Кэшировать скомпилированный JSX и HTML для ускорения рендеринга.
  5. Разделять контент на страницы или секции, чтобы сервер обрабатывал меньшие фрагменты и уменьшал нагрузку.

Эта схема позволяет полностью использовать возможности MDX в серверной среде, сочетая гибкость JSX с производительностью SSR и стабильной выдачей готового HTML.