markdown-it vs remarkable vs showdown
前端 Markdown 解析器选型对比
markdown-itremarkableshowdown类似的npm包:

前端 Markdown 解析器选型对比

markdown-itremarkableshowdown 都是用于将 Markdown 文本转换为 HTML 的 JavaScript 库,适用于在浏览器或 Node.js 环境中渲染用户输入的 Markdown 内容。它们都支持标准 CommonMark 语法,并提供扩展机制以支持自定义功能(如任务列表、脚注、高亮等)。这些库广泛用于博客系统、文档站点、富文本编辑器预览和内容管理系统中,核心目标是在保证安全性和性能的前提下,提供灵活、可配置的 Markdown 渲染能力。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
markdown-it23,363,67221,479777 kB202 天前MIT
remarkable05,829-1326 年前MIT
showdown014,858801 kB235-MIT

Markdown 解析器深度对比:markdown-it vs remarkable vs showdown

在前端开发中,将用户输入的 Markdown 安全高效地渲染为 HTML 是常见需求。markdown-itremarkableshowdown 是三个主流选择,但它们在架构、扩展性和维护状态上差异显著。本文从工程实践角度,对比三者的核心能力。

⚙️ 架构与扩展机制:插件化 vs 选项配置 vs 固定功能

markdown-it 采用基于插件的模块化架构,所有核心功能(包括链接、图片)都可通过插件启用或替换。它暴露完整的解析器(parser)、渲染器(renderer)和规则链(rules),允许深度干预解析过程。

// markdown-it: 插件扩展
import MarkdownIt from 'markdown-it';
import markdownItTaskLists from 'markdown-it-task-lists';

const md = new MarkdownIt()
  .use(markdownItTaskLists); // 启用任务列表插件

const html = md.render('- [x] done');

remarkable 提供有限的内置扩展(如 breakslinkify),但不支持第三方插件。其扩展需通过直接修改内部规则实现,灵活性较低。

// remarkable: 内置选项配置
import Remarkable from 'remarkable';

const md = new Remarkable({
  breaks: true,        // 转换 \n 为 <br>
  linkify: true        // 自动识别 URL
});

const html = md.render('Visit https://example.com');

showdown 使用选项(options)和扩展(extensions)两种方式。扩展需通过全局注册或实例方法添加,但 API 较为冗长。

// showdown: 扩展注册
import showdown from 'showdown';

showdown.extension('myext', function() {
  return [
    {
      type: 'output',
      filter: function(text) {
        return text.replace(/@\w+/g, '<code>$&</code>');
      }
    }
  ];
});

const converter = new showdown.Converter({ extensions: ['myext'] });
const html = converter.makeHtml('Mention @user');

💡 关键区别markdown-it 的插件机制最灵活,适合复杂定制;showdown 的扩展需手动编写过滤器;remarkable 几乎无法扩展新语法。

🛡️ 安全性:XSS 防护能力

三者默认均不自动转义 HTML 标签,这意味着如果用户输入包含 <script>,会直接输出到 HTML 中,造成 XSS 风险。开发者必须显式启用转义或使用 sanitize 库。

markdown-it 推荐通过 html: false 禁用原生 HTML,并配合 markdown-it-sanitizer 等插件处理。

// markdown-it: 禁用 HTML + 安全插件
const md = new MarkdownIt({ html: false })
  .use(require('markdown-it-sanitizer'));

remarkable 同样需设置 html: false,但无官方安全插件。

// remarkable: 禁用 HTML
const md = new Remarkable({ html: false });

showdown 通过 noHeaderIdghCompatibleHeaderId 等选项间接提升安全,但核心仍需依赖 sanitize-html 等外部库。

// showdown: 配合 sanitize-html
import sanitizeHtml from 'sanitize-html';
const dirty = converter.makeHtml(userInput);
const clean = sanitizeHtml(dirty);

⚠️ 重要提醒:无论选择哪个库,都必须额外处理 XSS 防护。不要依赖解析器默认行为。

🧪 语法兼容性与标准遵循

  • markdown-it 严格遵循 CommonMark 0.30 规范,并通过官方测试套件验证。对 GFM(GitHub Flavored Markdown)的支持通过插件(如 markdown-it-gfm)实现。
  • remarkable 基于早期 CommonMark 草案,部分边缘情况(如嵌套列表、引用块)与标准存在差异。
  • showdown 兼容 CommonMark,但默认行为更接近传统 Markdown(如不支持表格),需开启 tables: true 等选项。
// 表格支持对比
const tableMd = '| a | b |\n|---|---|\n| 1 | 2 |';

// markdown-it: 需插件
md.use(require('markdown-it-tables'));

// showdown: 需选项
new showdown.Converter({ tables: true });

// remarkable: 不支持表格(需手动 hack)

🚫 维护状态:是否适合新项目?

  • remarkable已停止维护。其 GitHub 仓库 README 明确写道:“此项目不再积极开发。推荐使用 markdown-it。” 新项目应避免选用。
  • markdown-itshowdown 均处于活跃维护状态,定期发布更新并修复安全问题。

🔧 实际工程场景推荐

场景 1:构建文档站点(如 Docusaurus 替代方案)

  • 需求:支持 GFM、代码高亮、自定义容器(如 :::tip)、SEO 友好。
  • 推荐markdown-it
  • 理由:丰富的插件生态(markdown-it-highlightjsmarkdown-it-container)可快速实现所需功能,且 AST 支持便于静态分析。
// markdown-it 示例:自定义容器
md.use(require('markdown-it-container'), 'tip', {
  validate: params => params.trim().match(/^tip\s+(.*)$/),
  render: (tokens, idx) => {
    if (tokens[idx].nesting === 1) {
      return '<div class="tip">';
    } else {
      return '</div>';
    }
  }
});

场景 2:用户评论系统(简单 Markdown 支持)

  • 需求:基础语法(粗体、链接、列表)、自动转义、轻量级。
  • 推荐showdown
  • 理由:配置简单,无需复杂插件,配合 sanitize-html 即可满足安全需求。
// showdown 示例:基础配置
const converter = new showdown.Converter({
  simpleLineBreaks: true,
  strikethrough: true
});

场景 3:遗留系统维护

  • 现状:已有 remarkable 集成,功能稳定无扩展需求。
  • 建议:继续使用,但不要在新模块中引入。规划迁移至 markdown-it

📊 总结:核心差异速查表

特性markdown-itremarkableshowdown
扩展机制插件系统(高度灵活)内置选项(不可扩展)扩展 + 选项
CommonMark 兼容✅ 严格遵循⚠️ 部分兼容✅(需配置)
维护状态✅ 活跃❌ 已停止✅ 活跃
XSS 防护需手动配置 + 插件需手动配置需手动配置 + 外部库
适用场景复杂定制、文档平台遗留系统快速集成、简单需求

💡 最终建议

  • 新项目首选 markdown-it:它的插件生态和标准兼容性为未来扩展留足空间。
  • 避免 remarkable:除非维护旧代码,否则不要引入已废弃的依赖。
  • showdown 作为轻量备选:当项目简单、团队熟悉其 API 时可考虑,但需自行处理安全和高级功能。

无论选择哪个库,请始终记住:Markdown 解析只是第一步,安全渲染才是关键

如何选择: markdown-it vs remarkable vs showdown

  • markdown-it:

    选择 markdown-it 如果你需要高度可扩展的解析器,支持插件生态、细粒度的 AST 操作,以及对 CommonMark 标准的严格遵循。它适合构建需要深度定制(如自定义语法、安全过滤、语法高亮集成)的复杂应用,例如文档平台或开发者工具。

  • remarkable:

    选择 remarkable 如果你追求极致的解析速度和简洁的 API,且不需要频繁扩展功能。但需注意:该库已不再积极维护,官方 GitHub 仓库明确建议新项目使用 markdown-it。因此,仅在遗留系统维护或对性能有极端要求且功能固定的场景下考虑。

  • showdown:

    选择 showdown 如果你需要一个简单易用、开箱即用的解析器,且偏好基于选项(options)而非插件的配置方式。它适合快速集成到中小型项目中(如评论系统、静态站点生成器),尤其当你不需要复杂的 AST 操作或高性能要求时。

markdown-it的README

markdown-it

CI NPM version Coverage Status

Markdown parser done right. Fast and easy to extend.

Live demo

  • Follows the CommonMark spec + adds syntax extensions & sugar (URL autolinking, typographer).
  • Configurable syntax! You can add new rules and even replace existing ones.
  • High speed.
  • Safe by default.
  • Community-written plugins and other packages on npm.

Table of content

Install

node.js:

npm install markdown-it

[!NOTE]

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, wich mirror npm registry

Usage examples

See also:

Simple

// node.js
// can use `require('markdown-it')` for CJS
import markdownit from 'markdown-it'
const md = markdownit()
const result = md.render('# markdown-it rulezz!');

// browser with UMD build, added to "window" on script load
// Note, there is no dash in "markdownit".
const md = window.markdownit();
const result = md.render('# markdown-it rulezz!');

Single line rendering, without paragraph wrap:

import markdownit from 'markdown-it'
const md = markdownit()
const result = md.renderInline('__markdown-it__ rulezz!');

Init with presets and options

(*) presets define combinations of active rules and options. Can be "commonmark", "zero" or "default" (if skipped). See API docs for more details.

import markdownit from 'markdown-it'

// commonmark mode
const md = markdownit('commonmark')

// default mode
const md = markdownit()

// enable everything
const md = markdownit({
  html: true,
  linkify: true,
  typographer: true
})

// full options list (defaults)
const md = markdownit({
  // Enable HTML tags in source
  html:         false,

  // Use '/' to close single tags (<br />).
  // This is only for full CommonMark compatibility.
  xhtmlOut:     false,

  // Convert '\n' in paragraphs into <br>
  breaks:       false,

  // CSS language prefix for fenced blocks. Can be
  // useful for external highlighters.
  langPrefix:   'language-',

  // Autoconvert URL-like text to links
  linkify:      false,

  // Enable some language-neutral replacement + quotes beautification
  // For the full list of replacements, see https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.mjs
  typographer:  false,

  // Double + single quotes replacement pairs, when typographer enabled,
  // and smartquotes on. Could be either a String or an Array.
  //
  // For example, you can use '«»„“' for Russian, '„“‚‘' for German,
  // and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
  quotes: '“”‘’',

  // Highlighter function. Should return escaped HTML,
  // or '' if the source string is not changed and should be escaped externally.
  // If result starts with <pre... internal wrapper is skipped.
  highlight: function (/*str, lang*/) { return ''; }
});

Plugins load

import markdownit from 'markdown-it'

const md = markdownit()
  .use(plugin1)
  .use(plugin2, opts, ...)
  .use(plugin3);

Syntax highlighting

Apply syntax highlighting to fenced code blocks with the highlight option:

import markdownit from 'markdown-it'
import hljs from 'highlight.js' // https://highlightjs.org

// Actual default values
const md = markdownit({
  highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return hljs.highlight(str, { language: lang }).value;
      } catch (__) {}
    }

    return ''; // use external default escaping
  }
});

Or with full wrapper override (if you need assign class to <pre> or <code>):

import markdownit from 'markdown-it'
import hljs from 'highlight.js' // https://highlightjs.org

// Actual default values
const md = markdownit({
  highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return '<pre><code class="hljs">' +
               hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
               '</code></pre>';
      } catch (__) {}
    }

    return '<pre><code class="hljs">' + md.utils.escapeHtml(str) + '</code></pre>';
  }
});

Linkify

linkify: true uses linkify-it. To configure linkify-it, access the linkify instance through md.linkify:

md.linkify.set({ fuzzyEmail: false });  // disables converting email to link

API

API documentation

If you are going to write plugins, please take a look at Development info.

Syntax extensions

Embedded (enabled by default):

Via plugins:

Manage rules

By default all rules are enabled, but can be restricted by options. On plugin load all its rules are enabled automatically.

import markdownit from 'markdown-it'

// Activate/deactivate rules, with currying
const md = markdownit()
  .disable(['link', 'image'])
  .enable(['link'])
  .enable('image');

// Enable everything
const md = markdownit({
  html: true,
  linkify: true,
  typographer: true,
});

You can find all rules in sources:

Benchmark

Here is the result of readme parse at MB Pro Retina 2013 (2.4 GHz):

npm run benchmark-deps
benchmark/benchmark.mjs readme

Selected samples: (1 of 28)
 > README

Sample: README.md (7774 bytes)
 > commonmark-reference x 1,222 ops/sec ±0.96% (97 runs sampled)
 > current x 743 ops/sec ±0.84% (97 runs sampled)
 > current-commonmark x 1,568 ops/sec ±0.84% (98 runs sampled)
 > marked x 1,587 ops/sec ±4.31% (93 runs sampled)

[!NOTE]

CommonMark version runs with simplified link normalizers for more "honest" compare. Difference is ≈1.5×.

As you can see, markdown-it doesn't pay with speed for its flexibility. Slowdown of "full" version caused by additional features not available in other implementations.

Authors

markdown-it is the result of the decision of the authors who contributed to 99% of the Remarkable code to move to a project with the same authorship but new leadership (Vitaly and Alex). It's not a fork.

References / Thanks

Big thanks to John MacFarlane for his work on the CommonMark spec and reference implementations. His work saved us a lot of time during this project's development.

Related Links:

Ports