markdown-it and react-markdown are both popular tools for processing Markdown content, but they serve different architectural roles in a frontend stack. markdown-it is a high-performance, framework-agnostic Markdown parser that converts text into HTML strings. It is highly extensible via plugins and gives developers full control over the parsing rules. react-markdown, on the other hand, is a React component designed to render Markdown safely within a React tree. It uses remark and rehype under the hood to transform Markdown into React elements rather than raw HTML strings, offering better integration with React's rendering lifecycle and stricter security defaults.
Both markdown-it and react-markdown solve the problem of displaying Markdown content, but they approach it from opposite ends of the spectrum. markdown-it is a classic string-to-HTML parser, while react-markdown is a React component that builds a virtual DOM tree. Understanding this core difference is critical for making the right architectural choice.
markdown-it parses Markdown text and returns a raw HTML string.
dangerouslySetInnerHTML.// markdown-it: Returns an HTML string
import MarkdownIt from 'markdown-it';
const md = new MarkdownIt();
const html = md.render('# Hello World');
// In a React component
<div dangerouslySetInnerHTML={{ __html: html }} />
react-markdown parses Markdown and returns React elements.
dangerouslySetInnerHTML.// react-markdown: Returns React elements
import ReactMarkdown from 'react-markdown';
function Post({ content }) {
return <ReactMarkdown>{content}</ReactMarkdown>;
}
// Usage
<Post content="# Hello World" />
Security is the biggest differentiator. Markdown often allows embedded HTML, which can lead to Cross-Site Scripting (XSS) attacks if not handled correctly.
markdown-it allows HTML tags by default.
DOMPurify when using dangerouslySetInnerHTML.// markdown-it: HTML enabled by default (Risky)
const md = new MarkdownIt();
// To disable HTML:
const mdSafe = new MarkdownIt({ html: false });
// Even with html: false, you should sanitize the output
import DOMPurify from 'dompurify';
const cleanHtml = DOMPurify.sanitize(md.render(input));
react-markdown strips HTML tags by default.
rehype-raw).// react-markdown: HTML stripped by default (Safe)
<ReactMarkdown>{input}</ReactMarkdown>
// To allow HTML, you must opt-in explicitly
import rehypeRaw from 'rehype-raw';
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{input}</ReactMarkdown>
Both libraries are extensible, but the mechanism differs based on their output type.
markdown-it uses a plugin system to modify parsing rules.
// markdown-it: Using plugins for syntax
import MarkdownIt from 'markdown-it';
import markdownItEmoji from 'markdown-it-emoji';
const md = new MarkdownIt().use(markdownItEmoji);
const html = md.render('Hello :smile:');
// Output: Hello <span class="emoji">π</span>
react-markdown allows overriding specific components.
h1 or a) to custom React components.components prop to inject custom logic.// react-markdown: Overriding components
import ReactMarkdown from 'react-markdown';
const CustomHeading = ({ children }) => <h1 className="custom">{children}</h1>;
<ReactMarkdown
components={{ h1: CustomHeading }}
>
{content}
</ReactMarkdown>
Handling code blocks and styling is a common requirement for documentation sites.
markdown-it relies on external highlighters.
highlight.js or Prism alongside it.// markdown-it: Syntax highlighting setup
import hljs from 'highlight.js';
import MarkdownIt from 'markdown-it';
const md = new MarkdownIt({
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(str, { language: lang }).value;
}
return '';
}
});
react-markdown uses rehype plugins for highlighting.
rehype-highlight or rehype-prism-plus are common choices.// react-markdown: Syntax highlighting via plugin
import ReactMarkdown from 'react-markdown';
import rehypeHighlight from 'rehype-highlight';
<ReactMarkdown rehypePlugins={[rehypeHighlight]}>
{content}
</ReactMarkdown>
Despite their differences, both libraries share common goals and capabilities.
// Both render standard Markdown identically
const text = "- Item 1\n- Item 2";
// markdown-it: md.render(text) -> <ul>...</ul>
// react-markdown: <ReactMarkdown>{text}</ReactMarkdown> -> <ul>...</ul>
markdown-it has npm plugins for parsing.react-markdown leverages the unified ecosystem (remark/rehype).// markdown-it plugin
md.use(require('markdown-it-deflist'));
// react-markdown plugin
<ReactMarkdown remarkPlugins={[require('remark-deflist')]} />
react-markdown supports async plugins for data fetching during render.markdown-it is generally synchronous but can be wrapped.// react-markdown: Async plugin support
const asyncPlugin = async () => { /* fetch data */ };
<ReactMarkdown remarkPlugins={[asyncPlugin]} />
| Feature | markdown-it | react-markdown |
|---|---|---|
| Output Type | π HTML String | βοΈ React Elements |
| React Integration | β οΈ Requires dangerouslySetInnerHTML | β Native Component |
| Security | π HTML allowed by default | π HTML stripped by default |
| Customization | π Parser Plugins | π§© Component Overrides |
| Framework | π Agnostic (Node, Vue, React) | βοΈ React Only |
| Ecosystem | π¦ markdown-it plugins | π¦ unified / remark / rehype |
markdown-it is like a powerful engine ποΈ β it gives you raw speed and control over the output string. It is ideal for backend rendering, static site generation where you control the HTML pipeline, or non-React frontends. However, you must build your own safety rails to prevent XSS attacks.
react-markdown is like a pre-fabricated home π β it is designed to fit perfectly into a React application. It prioritizes security and developer experience by handling the complex parts of the React lifecycle for you. It is the default choice for modern React dashboards, blogs, and documentation sites.
Final Thought: If you are in React, start with react-markdown for safety and ease of use. Only reach for markdown-it if you have specific needs for HTML string manipulation or are working outside the React ecosystem.
Choose markdown-it if you need a framework-agnostic parser, require maximum control over the generated HTML, or are working outside of React (e.g., Node.js backend, Vue, or vanilla JS). It is ideal when you need to sanitize the output yourself or when you want to leverage its vast ecosystem of plugins for syntax highlighting and custom rules. Be prepared to handle XSS risks manually when injecting the resulting HTML into the DOM.
Choose react-markdown if you are building a React application and want a secure, drop-in component that handles sanitization by default. It is the better choice when you need to override specific HTML tags with custom React components (like linking headers for anchor tags) without dealing with dangerouslySetInnerHTML. Opt for this when developer safety and React integration are higher priorities than raw HTML string manipulation.
Markdown parser done right. Fast and easy to extend.
[!NOTE] If you are upgrading to v15, see the migration guide.
npm install markdown-it
For a quick look at dist/ folder contents, see https://unpkg.com/markdown-it/.
For browser you can use unpkg.com, esm.sh or any other CDN, which mirror npm
registry.
import MarkdownIt from 'markdown-it'
const md = new MarkdownIt()
const result = md.render('# markdown-it rulezz!')