markdown-it、remarkable、showdown はいずれもJavaScriptでMarkdownをHTMLに変換するためのライブラリです。これらは共通してCommonMark準拠や拡張性を重視しつつ、それぞれ異なる設計哲学とAPIスタイルを持っています。markdown-itはプラグインによる柔軟な拡張を特徴とし、remarkableは高速かつ安全な出力を目指して開発されましたが、現在は非推奨となっています。showdownは長年にわたり安定した実績を持ち、シンプルなAPIと豊富なオプション設定が特徴です。
フロントエンド開発でMarkdownをHTMLに変換する必要があるとき、markdown-it、remarkable、showdownはよく検討される選択肢です。しかし、それぞれの設計思想、拡張性、メンテナンス状況には大きな違いがあります。この記事では、実際のコード例を交えながら、技術的な観点から深く比較します。
まず重要なのは、remarkableは非推奨であることです。npmページおよびGitHubリポジトリには明確に「DEPRECATED」と記載されており、2017年以降の更新はありません。新規プロジェクトでの使用は避けてください。
一方、markdown-itとshowdownはともにアクティブにメンテナンスされており、TypeScript定義や最新のセキュリティ対応も整っています。
markdown-it: プラグインファースト設計markdown-itは、コア機能を最小限に抑え、ほぼすべての機能をプラグインで提供します。たとえば、テーブルや脚注を有効にするには別途プラグインをインストール・登録する必要があります。
// markdown-it: プラグインで機能を追加
import MarkdownIt from 'markdown-it';
import markdownItTable from 'markdown-it-table';
const md = new MarkdownIt().use(markdownItTable);
const html = md.render('| A | B |\n|---|---|\n| 1 | 2 |');
// 出力: <table>...</table>
この設計により、不要な機能を含めずに軽量に保てます。また、独自のルール(トークン)を追加することも容易です。
showdown: ビルトインオプションと拡張ポイントshowdownは多くの機能をオプションとして内蔵しています。たとえば、GitHub風のコードブロックやテーブルはフラグを立てるだけで有効になります。
// showdown: オプションで機能を有効化
import showdown from 'showdown';
const converter = new showdown.Converter({
tables: true,
ghCodeBlocks: true
});
const html = converter.makeHtml('| A | B |\n|---|---|\n| 1 | 2 |');
// 出力: <table>...</table>
さらに、addExtensionメソッドでカスタム拡張も可能です。
// showdown: カスタム拡張
showdown.extension('myExt', function() {
return [
{
type: 'output',
regex: /MY_TOKEN/g,
replace: '<span class="my-token">MY_TOKEN</span>'
}
];
});
const converter = new showdown.Converter({ extensions: ['myExt'] });
remarkable: 非推奨のため拡張性は考慮不要remarkableもかつては拡張可能でしたが、現在はメンテナンスされていないため、新規開発での採用は推奨されません。
// remarkable: 非推奨のため使用しない
// const Remarkable = require('remarkable');
// const md = new Remarkable();
MarkdownはHTMLに変換されるため、悪意ある入力からXSSを防ぐ必要があります。
markdown-it: コアではサニタイズを行いませんが、markdown-it-sanitizerなどのサードパーティプラグインで対応可能です。// markdown-it + サニタイズプラグイン
import MarkdownIt from 'markdown-it';
import sanitizer from 'markdown-it-sanitizer';
const md = new MarkdownIt().use(sanitizer);
showdown: sanitizeオプションをtrueにすることで、基本的なXSS対策が有効になります。// showdown: 組み込みサニタイズ
const converter = new showdown.Converter({ sanitize: true });
const html = converter.makeHtml(')');
// 出力: <p>![img]()</p> (危険な部分が除去される)
remarkable: サニタイズ機能はありますが、非推奨のため信頼性に欠けます。高度な処理(例:静的解析、カスタムレンダリング)を行う場合、ASTへのアクセスが重要です。
markdown-it: parseメソッドでトークン配列(AST相当)を取得できます。// markdown-it: AST取得
const tokens = md.parse('# Hello', {});
console.log(tokens);
// [{ type: 'heading_open', ... }, { type: 'inline', ... }, ...]
showdown: 標準ではASTを公開していません。内部のparserやtokenizerは非公開APIのため、直接操作は推奨されません。
remarkable: parseメソッドでASTを取得可能でしたが、非推奨のため使用できません。
markdown-itが最適です。ASTへのアクセスと細かいイベントフックにより、カーソル位置に応じたプレビュー表示や構文強調が容易に実装できます。
showdownが手軽です。オプション一発でテーブルやコードブロックが有効になり、サニタイズも組み込みで対応できます。
remarkable利用プロジェクト早急にmarkdown-itまたはshowdownへの移行を検討してください。非推奨ライブラリのまま運用を続けると、セキュリティリスクや依存関係の断絶が発生します。
| 観点 | markdown-it | showdown | remarkable |
|---|---|---|---|
| メンテナンス | ✅ アクティブ | ✅ アクティブ | ❌ 非推奨 |
| 拡張性 | ⭐⭐⭐ プラグイン中心 | ⭐⭐ オプション+拡張 | ⭐(非推奨) |
| セキュリティ | プラグイン依存 | 組み込みサニタイズ | 古い実装 |
| ASTアクセス | ✅ 公式API | ❌ 非公開 | ✅(非推奨) |
| 学習コスト | 中〜高 | 低〜中 | 低(非推奨) |
remarkableを絶対に使わないでください。markdown-itshowdownどちらを選ぶにせよ、ユーザー入力からのXSSリスクを常に意識し、適切なサニタイズを実装することが必須です。
markdown-itは、高度なカスタマイズや拡張が必要なプロジェクトに最適です。公式およびコミュニティ製の多数のプラグインがあり、構文ハイライト、テーブル、脚注など、幅広い機能を簡単に追加できます。AST(抽象構文木)へのアクセスも可能で、静的解析や変換処理にも対応します。大規模アプリやエディタ統合など、柔軟性が求められる場面で選ぶべきです。
showdownはシンプルで安定したMarkdown変換を必要とするプロジェクトに向いています。多くのオプション設定(例:ghCodeBlocks、tables)をビルトインで提供し、プラグイン機構も備えています。TypeScriptサポートも整っており、ドキュメントも充実しています。ただし、拡張性やパフォーマンス面ではmarkdown-itに劣る場合があるため、中規模以下の用途や既存システムとの互換性を重視する場合に適しています。
remarkableは2017年以降更新が停止されており、npmおよびGitHub上で非推奨(deprecated)と明記されています。新規プロジェクトでは使用しないでください。既存プロジェクトで利用している場合は、markdown-itやshowdownへの移行を検討すべきです。過去には高速かつ安全な出力が評価されていましたが、現在はメンテナンスされていないため脆弱性リスクがあります。
Markdown parser done right. Fast and easy to extend.
Table of content
node.js:
npm install markdown-it
browser (CDN):
See also:
// 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!');
(*) 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 ''; }
});
import markdownit from 'markdown-it'
const md = markdownit
.use(plugin1)
.use(plugin2, opts, ...)
.use(plugin3);
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: 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
If you are going to write plugins, please take a look at Development info.
Embedded (enabled by default):
Via plugins:
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:
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.
Available as part of the Tidelift Subscription.
The maintainers of markdown-it and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.
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.
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