markdown-it, marked, remarkable, and showdown are all JavaScript libraries designed to convert Markdown text into HTML. They serve as the core engine for rendering user-generated content, documentation sites, and comment systems in web applications. While they share the same goal, they differ significantly in architecture, plugin support, CommonMark compliance, and security features. markdown-it is known for its extreme extensibility and plugin ecosystem. marked focuses on speed and simplicity. showdown offers a balance with built-in extensions. remarkable was once popular but has fallen behind in maintenance compared to the others.
When building web applications that accept user content, you need a reliable way to turn Markdown into HTML. The four main contenders in the JavaScript ecosystem are markdown-it, marked, remarkable, and showdown. They all solve the same problem, but they take very different approaches to speed, security, and customization. Let's look at how they handle real-world engineering tasks.
Getting started varies slightly between libraries. Some focus on zero-config simplicity, while others expect you to define rules upfront.
markdown-it requires you to create an instance and often enable specific features manually for security or compliance.
// markdown-it: Explicit instance creation
const md = require('markdown-it')();
const result = md.render('# Hello World');
marked is designed to work immediately with a single function call.
// marked: Direct function call
const { marked } = require('marked');
const result = marked('# Hello World');
remarkable follows a class-based instantiation similar to markdown-it.
// remarkable: Class instantiation
const { Remarkable } = require('remarkable');
const md = new Remarkable();
const result = md.render('# Hello World');
showdown uses a converter object that can be configured with global options.
// showdown: Converter object
const { Converter } = require('showdown');
const converter = new Converter();
const result = converter.makeHtml('# Hello World');
Real-world apps rarely use plain Markdown. You often need tables, task lists, or custom syntax. This is where the architectures diverge.
markdown-it has the most powerful plugin system. You can modify the token stream directly.
// markdown-it: Plugin usage
const md = require('markdown-it')();
md.use(require('markdown-it-emoji'));
const result = md.render(':smile:');
marked supports extensions but requires more manual setup for custom renderers.
// marked: Custom renderer
const { marked } = require('marked');
const renderer = new marked.Renderer();
renderer.heading = (text, level) => `<h${level} class="custom">${text}</h${level}>`;
const result = marked('# Hello', { renderer });
remarkable allows plugins but the ecosystem is much smaller and less active.
// remarkable: Plugin usage
const { Remarkable } = require('remarkable');
const md = new Remarkable();
md.use((md) => {
// Custom rule logic here
});
const result = md.render('# Hello');
showdown comes with many extensions built-in or easily activated via options.
// showdown: Built-in extensions
const { Converter } = require('showdown');
const converter = new Converter({ extensions: ['tables', 'github'] });
const result = converter.makeHtml('| col |\n| --- |');
Rendering user input means facing XSS risks. None of these libraries sanitize HTML by default โ you must pair them with a sanitizer.
markdown-it does not sanitize output. You must use a library like dompurify.
// markdown-it: Requires external sanitizer
const md = require('markdown-it')();
const DOMPurify = require('dompurify');
const rawHtml = md.render('[xss](javascript:alert(1))');
const cleanHtml = DOMPurify.sanitize(rawHtml);
marked previously had a sanitize option but deprecated it. External sanitization is now required.
// marked: External sanitizer required
const { marked } = require('marked');
const DOMPurify = require('dompurify');
const rawHtml = marked('[xss](javascript:alert(1))');
const cleanHtml = DOMPurify.sanitize(rawHtml);
remarkable also lacks built-in sanitization.
// remarkable: External sanitizer required
const { Remarkable } = require('remarkable');
const md = new Remarkable();
const DOMPurify = require('dompurify');
const cleanHtml = DOMPurify.sanitize(md.render('[xss](javascript:alert(1))'));
showdown has a simpleSpanBool and other options but still recommends external sanitization for untrusted input.
// showdown: External sanitizer recommended
const { Converter } = require('showdown');
const converter = new Converter();
const DOMPurify = require('dompurify');
const cleanHtml = DOMPurify.sanitize(converter.makeHtml('[xss](javascript:alert(1))'));
CommonMark is the standard specification for Markdown. Compliance ensures your text renders the same way across different tools.
markdown-it is fully CommonMark compliant by default. It passes almost all standard tests.
// markdown-it: High compliance
const md = require('markdown-it')({ html: true, linkify: true });
// Renders strictly according to CommonMark spec
marked aims for compliance but prioritizes speed. Some edge cases may differ from the spec.
// marked: Good compliance
const { marked } = require('marked');
// Generally follows spec but may optimize for performance
remarkable was built with CommonMark in mind but lags behind due to lack of updates.
// remarkable: Moderate compliance
const { Remarkable } = require('remarkable');
// May miss newer spec updates due to maintenance status
showdown supports a "GitHub flavored" style which deviates from strict CommonMark in favor of features.
// showdown: Flavor focused
const { Converter } = require('showdown');
// Prioritizes GitHub style over strict CommonMark adherence
For advanced developers, how the library handles tokens matters. This affects how you can customize the output.
markdown-it exposes the full token stream. You can iterate and modify tokens before rendering.
// markdown-it: Token manipulation
const md = require('markdown-it')();
const tokens = md.parse('# Hello');
tokens.forEach((token) => {
if (token.type === 'heading_open') token.attrJoin('class', 'title');
});
const result = md.renderer.render(tokens, md.options);
marked uses a lexer and parser but exposes less control over the intermediate token stream in recent versions.
// marked: Lexer usage
const { marked } = require('marked');
const tokens = marked.lexer('# Hello');
// Tokens are available but modifying them is less direct
remarkable provides token access but the API is less documented.
// remarkable: Token access
const { Remarkable } = require('remarkable');
const md = new Remarkable();
const tokens = md.parse('# Hello');
// Limited documentation on token manipulation
showdown focuses on regex-based replacements rather than a full token tree.
// showdown: Regex based
const { Converter } = require('showdown');
// Internally uses regex chains rather than a token AST
One of these packages is effectively frozen in time.
remarkable has not seen significant updates in years. It is not officially deprecated on npm, but the repository shows minimal activity. Using it introduces risk for security vulnerabilities and lack of support for new Markdown features.
// remarkable: Legacy warning
// Do not start new projects with this package
const { Remarkable } = require('remarkable');
The other three (markdown-it, marked, showdown) are actively maintained and safe for production use.
| Feature | markdown-it | marked | remarkable | showdown |
|---|---|---|---|---|
| Speed | Fast | Very Fast | Moderate | Moderate |
| Extensibility | ๐ฅ High (Plugin ecosystem) | Medium (Custom renderers) | Low (Stalled) | Medium (Built-in extensions) |
| Compliance | โ Strict CommonMark | โ ๏ธ Near CommonMark | โ ๏ธ Outdated CommonMark | ๐จ GitHub Flavored |
| Token Access | โ Full Tree | โ ๏ธ Limited | โ ๏ธ Limited | โ Regex Based |
| Maintenance | โ Active | โ Active | โ Inactive | โ Active |
markdown-it is the power user's choice ๐ง. If you need to build a complex editor, a documentation platform with custom syntax, or an enterprise CMS, this is the tool. Its plugin system is unmatched.
marked is the speed demon ๐๏ธ. For simple blogs, README rendering, or high-throughput parsing where standard Markdown is enough, it wins on performance and ease of use.
showdown is the balanced option โ๏ธ. It sits in the middle, offering GitHub-style features out of the box without the complexity of markdown-it.
remarkable is the legacy option ๐ฐ๏ธ. Do not use it for new work. It serves only those maintaining older systems.
Final Thought: For most modern frontend architectures, markdown-it offers the best long-term value due to its extensibility, while marked is perfect for quick, lightweight needs. Avoid remarkable to future-proof your stack.
Choose markdown-it if you need maximum flexibility and a rich plugin ecosystem. It is the best fit for complex applications where you need to customize the parsing logic or add custom syntax rules. Its architecture allows you to swap out tokens and modify the render tree, making it ideal for enterprise-grade content systems.
Choose marked if you prioritize speed and simplicity above all else. It works out of the box with zero configuration for standard Markdown. This package is perfect for lightweight applications, static site generators, or scenarios where you need to parse large volumes of text quickly without custom extensions.
Avoid using remarkable for new projects. While it was once a strong contender, it is no longer actively maintained compared to markdown-it or marked. The lack of recent updates means it may miss security patches or modern CommonMark features. Only consider it if you are maintaining a legacy codebase that already depends on it.
Choose showdown if you want a middle ground with built-in extensions like tables and GitHub-style syntax without needing external plugins. It is a solid choice for applications that need specific flavor features out of the box but do not require the deep architectural customization of markdown-it.
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!')