Интеграция с React

Для начала работы с Markdown-it в проекте на React необходимо установить сам пакет и, при необходимости, дополнительные плагины:

npm install markdown-it

После установки создаётся экземпляр Markdown-it, который можно настроить под конкретные требования. Например:

import MarkdownIt from 'markdown-it';

const md = new MarkdownIt({
  html: true,          // Разрешает HTML-теги в Markdown
  linkify: true,       // Автоматически превращает URL в ссылки
  typographer: true,   // Включает типографские преобразования (кавычки, тире)
});

Каждая из этих опций может значительно изменить поведение парсера, поэтому важно понимать, какие из них нужны в приложении.


Использование Markdown-it в компоненте React

Для интеграции с React чаще всего создают компонент, который принимает Markdown-текст и рендерит HTML. Пример такого компонента:

import React from 'react';
import MarkdownIt from 'markdown-it';

const md = new MarkdownIt({ html: true, linkify: true, typographer: true });

const MarkdownRenderer = ({ content }) => {
  const renderedHTML = md.render(content);

  return (
    <div dangerouslySetInnerHTML={{ __html: renderedHTML }} />
  );
};

export default MarkdownRenderer;

Ключевой момент: использование dangerouslySetInnerHTML необходимо для вывода HTML, сгенерированного Markdown-it. Это открывает потенциальные риски XSS, поэтому важно убедиться в безопасном содержимом или использовать фильтры.


Плагины Markdown-it

Markdown-it имеет развитую систему плагинов, расширяющих возможности парсера. Примеры популярных плагинов:

  1. markdown-it-anchor — добавляет якоря к заголовкам для удобной навигации:
import MarkdownIt from 'markdown-it';
import markdownItAnchor from 'markdown-it-anchor';

const md = new MarkdownIt();
md.use(markdownItAnchor, {
  permalink: markdownItAnchor.permalink.ariaHidden({ symbol: '#' }),
  level: [1, 2, 3],
});
  1. markdown-it-footnote — поддержка сносок в тексте:
import markdownItFootnote from 'markdown-it-footnote';

md.use(markdownItFootnote);
  1. markdown-it-container — создание кастомных блоков с классами:
import markdownItContainer from 'markdown-it-container';

md.use(markdownItContainer, 'warning', {
  render(tokens, idx) {
    if (tokens[idx].nesting === 1) {
      return '<div class="warning">';
    } else {
      return '</div>';
    }
  }
});

Каждый плагин подключается методом .use() и может принимать собственные параметры для кастомизации рендеринга.


Работа с безопасным выводом

Для защиты от XSS рекомендуется использовать библиотеку DOMPurify совместно с Markdown-it:

import DOMPurify from 'dompurify';

const safeHTML = DOMPurify.sanitize(md.render(content));

return <div dangerouslySetInnerHTML={{ __html: safeHTML }} />;

Это особенно важно при отображении контента, введённого пользователями.


Асинхронная обработка и серверный рендеринг

Markdown-it поддерживает синхронный рендеринг, но при интеграции с React иногда требуется подготовка контента на сервере:

export async function getStaticProps() {
  const fs = require('fs');
  const path = require('path');
  const fileContent = fs.readFileSync(path.join(process.cwd(), 'content.md'), 'utf-8');

  const md = new MarkdownIt();
  const htmlContent = md.render(fileContent);

  return { props: { htmlContent } };
}

Использование такого подхода позволяет генерировать HTML заранее, сокращая нагрузку на клиент.


Настройка рендереров для элементов

Markdown-it предоставляет возможность переопределять рендеринг отдельных элементов. Например, кастомный рендеринг ссылок и изображений:

const defaultRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) {
  return self.renderToken(tokens, idx, options);
};

md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
  tokens[idx].attrPush(['target', '_blank']); // открытие в новой вкладке
  tokens[idx].attrPush(['rel', 'noopener noreferrer']);
  return defaultRender(tokens, idx, options, env, self);
};

Такой подход позволяет интегрировать Markdown-it в React-проекты с соблюдением требований безопасности и UX.


Интеграция с компонентной системой

Для сложных интерфейсов можно создавать компоненты React, которые рендерят отдельные Markdown-блоки, например:

const CustomBlock = ({ content }) => (
  <section className="custom-block">
    <MarkdownRenderer content={content} />
  </section>
);

Это облегчает повторное использование Markdown-контента и упрощает стилизацию с помощью CSS или CSS-in-JS.


Поддержка синтаксиса GitHub-Flavored Markdown

Markdown-it поддерживает расширения синтаксиса GitHub-Flavored Markdown через плагины:

import markdownItEmoji from 'markdown-it-emoji';
import markdownItTaskLists from 'markdown-it-task-lists';

md.use(markdownItEmoji);
md.use(markdownItTaskLists, { enabled: true });

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


Оптимизация производительности

Для больших документов рекомендуется:

  • Кэшировать результат рендеринга Markdown для часто используемых текстов.
  • Использовать useMemo в React-компонентах:
import { useMemo } from 'react';

const renderedHTML = useMemo(() => md.render(content), [content]);
  • Минимизировать количество подключаемых плагинов и отключать ненужные опции Markdown-it.

Поддержка мультимедийного контента

Для вставки изображений, видео или других медиа можно расширять Markdown через контейнеры или кастомные синтаксисы, обрабатываемые плагинами. Пример кастомного рендерера видео:

md.renderer.rules.video = function(tokens, idx) {
  const src = tokens[idx].attrGet('src');
  return `<video controls src="${src}"></video>`;
};

Это позволяет интегрировать мультимедийный контент без нарушения структуры React-приложения.


Вывод HTML как React-элементов

Для полного контроля можно преобразовывать Markdown в React-элементы, минуя dangerouslySetInnerHTML, используя парсинг токенов:

const renderTokens = (tokens) => tokens.map((token, idx) => {
  if (token.type === 'paragraph_open') return <p key={idx} />;
  if (token.type === 'inline') return token.content;
  if (token.type === 'paragraph_close') return </p>;
});

Этот способ сложнее, но позволяет использовать все преимущества React и избежать проблем с безопасностью.