marked vs markdown-it vs remark vs showdown vs micromark
JavaScript Markdown Parsing Libraries
markedmarkdown-itremarkshowdownmicromarkSimilar Packages:
JavaScript Markdown Parsing Libraries

markdown-it, marked, micromark, remark, and showdown are all JavaScript libraries for parsing Markdown and converting it to HTML, but they differ significantly in architecture, performance, extensibility, and use cases. markdown-it offers a flexible token-based pipeline with rich plugin support. marked provides a straightforward, fast parser with renderer customization. micromark is a low-level, streaming parser focused on spec compliance and performance. remark is part of the unified ecosystem, enabling powerful AST-based transformations through plugins. showdown uses a regex-driven approach and is best suited for simple or legacy use cases.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
marked21,692,56636,480433 kB252 months agoMIT
markdown-it13,138,64620,954767 kB572 years agoMIT
remark2,605,2708,70615.7 kB42 years agoMIT
showdown995,12014,798801 kB230-MIT
micromark02,132210 kB9a year agoMIT

Markdown Parsing in JavaScript: A Deep Dive into markdown-it, marked, micromark, remark, and showdown

When you need to render user-authored content or convert documentation from Markdown to HTML in a web application, choosing the right parser is critical. The five libraries — markdown-it, marked, micromark, remark, and showdown — all solve this problem but with very different architectures, extension models, and performance characteristics. Let’s break down how they work under the hood and where each shines.

🧱 Core Architecture: Tokenizers, Parsers, and Compilers

markdown-it uses a token-based pipeline: it parses Markdown into an array of tokens, then renders those tokens to HTML via rules. This makes it highly extensible because plugins can modify tokens at any stage.

// markdown-it: token-based rendering
import MarkdownIt from 'markdown-it';
const md = new MarkdownIt();
md.use(require('markdown-it-highlightjs'));
const html = md.render('# Hello'); // <h1>Hello</h1>

marked follows a direct AST-to-HTML approach. It parses Markdown into a syntax tree, then walks that tree to emit HTML strings. Extensions are added via custom renderer methods.

// marked: direct rendering with custom renderer
import { marked } from 'marked';
marked.use({
  renderer: {
    heading(text, level) {
      return `<h${level} class="heading">${text}</h${level}>`;
    }
  }
});
const html = marked.parse('# Hello');

micromark is a low-level, streaming parser built on finite-state machines. It doesn’t produce HTML directly — instead, it emits events that other tools (like mdast-util-from-markdown) consume to build syntax trees. It’s designed for composability and spec compliance.

// micromark: event-based parsing (requires additional utilities for HTML)
import { micromark } from 'micromark';
const html = micromark('# Hello'); // <h1>Hello</h1>
// For ASTs, you’d combine with `mdast-util-from-markdown` and `hast-util-to-html`

remark is not a parser itself — it’s a unified ecosystem that uses micromark under the hood. It builds an abstract syntax tree (MDAST), lets you transform it with plugins, then compiles it to HTML (via rehype). This makes it ideal for complex document processing pipelines.

// remark: AST transformation pipeline
import { remark } from 'remark';
import remarkHtml from 'remark-html';

const html = await remark()
  .use(remarkHtml)
  .process('# Hello'); // <h1>Hello</h1>

showdown uses a regex-heavy, string-replacement engine. It applies a series of regular expressions to convert Markdown constructs to HTML. While simple, this approach can struggle with edge cases and nested structures.

// showdown: string-based conversion
import showdown from 'showdown';
const converter = new showdown.Converter();
const html = converter.makeHtml('# Hello'); // <h1>Hello</h1>

🔌 Extensibility: How Plugins Work

Extending behavior varies dramatically:

  • markdown-it: Plugins register new rules or modify existing ones in the tokenizer/renderer. You get full control over token generation and HTML output.
// markdown-it plugin example
md.core.ruler.push('my_rule', function(state) {
  // manipulate state.tokens
});
  • marked: Extensions override specific renderer methods (e.g., link, image) or add custom tokenizer rules for new syntax.
// marked extension
marked.use({
  extensions: [{
    name: 'emoji',
    level: 'inline',
    start(src) { return src.indexOf(':'); },
    tokenizer(src) { /* ... */ },
    renderer(token) { return `<span>${token.text}</span>`; }
  }]
});
  • micromark: Extensions are low-level — they define new character codes, states, and effects. This is powerful but requires deep understanding of the parser’s internals.

  • remark: Plugins operate on the MDAST (Markdown AST). You can inspect, modify, or replace nodes using standard tree-walking patterns.

// remark plugin
function myPlugin() {
  return (tree) => {
    // mutate tree
  };
}
  • showdown: Extensions use regex to match patterns and return replacement strings. Limited to inline or block-level hooks.
// showdown extension
showdown.extension('myExt', function() {
  return [{
    type: 'lang',
    regex: /:emoji:(\w+)/,
    replace: '<span class="emoji">$1</span>'
  }];
});

⚡ Performance and Safety

All five libraries support basic XSS protection, but implementation differs:

  • markdown-it, marked, and showdown escape HTML by default but allow disabling it (risky!).
  • micromark and remark are safe by design — they never interpret raw HTML unless explicitly configured to do so via additional utilities.

For performance:

  • micromark is the fastest due to its optimized state machine.
  • marked and markdown-it are fast for typical use cases.
  • showdown can slow down on large documents due to regex backtracking.
  • remark has overhead from AST construction but enables powerful optimizations in complex workflows.

🛠️ Real-World Use Cases

Case 1: Simple Blog Comments

You just need to convert user comments from Markdown to sanitized HTML.

  • Best choice: marked or markdown-it
  • Why? Easy setup, good defaults, and sufficient security.
// marked for comments
import { marked } from 'marked';
marked.setOptions({ sanitize: false, // deprecated; use DOMPurify instead
                   gfm: true });
// Pair with DOMPurify for real security

Case 2: Documentation Site with Custom Syntax

You’re building a docs site that supports [note]This is a tip[/note] blocks.

  • Best choice: remark + custom plugin
  • Why? AST-based transforms make it easy to add new node types and render them consistently.
// remark handles custom syntax cleanly
remark()
  .use(customNotePlugin)
  .use(remarkHtml)

Case 3: High-Throughput API Converting Markdown

You’re processing thousands of Markdown files per second.

  • Best choice: micromark
  • Why? Minimal overhead, streaming support, and spec-compliant.

Case 4: Legacy CMS Migration

You’re replacing an old system that used showdown and must preserve exact output.

  • Only consider showdown if compatibility is non-negotiable.
  • Otherwise, migrate to a more robust parser.

📊 Summary Table

PackageArchitectureExtensibility ModelBest ForSecurity Approach
markdown-itToken pipelineRule-based pluginsFlexible rendering, rich featuresEscape by default
markedDirect AST → HTMLRenderer/Tokenizer hooksSimple, fast conversionEscape by default
micromarkStreaming FSMLow-level state machinesPerformance-critical parsingSafe by default
remarkUnified AST pipelineMDAST transformersComplex document processingSafe by default
showdownRegex replacementsString-replace extensionsLegacy compatibility onlyEscape by default (weak)

💡 Final Guidance

  • Need speed and correctness?micromark
  • Building a static site or CMS with custom Markdown?remark
  • Want a balance of features and simplicity?markdown-it or marked
  • Maintaining old code that uses showdown? → Keep it only if migration isn’t feasible; otherwise, move on.

All five are actively maintained as of 2024, but their design philosophies reflect different eras and priorities in the JavaScript ecosystem. Choose based on whether you value raw performance (micromark), transformation power (remark), ease of use (marked), flexibility (markdown-it), or legacy compatibility (showdown).

How to Choose: marked vs markdown-it vs remark vs showdown vs micromark
  • marked:

    Choose marked for straightforward, high-performance Markdown-to-HTML conversion with minimal setup. It’s ideal for blogs, comment systems, or any scenario where you need reliable rendering with light customization via renderer overrides. Avoid it if you require complex document transformations or strict CommonMark compliance beyond basic GitHub Flavored Markdown.

  • markdown-it:

    Choose markdown-it when you need a balance of performance, flexibility, and rich feature support. Its token-based architecture makes it easy to write plugins that modify parsing or rendering behavior, which is ideal for applications requiring custom syntax (like wikis or forums). It’s well-suited for client-side or server-side rendering where you want fine-grained control without the overhead of a full AST pipeline.

  • remark:

    Choose remark when you need to analyze, transform, or generate Markdown documents programmatically. Built on the unified ecosystem, it excels in complex workflows like documentation sites, linters, or content migration tools where you must manipulate the document structure before rendering. Be prepared for a steeper learning curve and more dependencies compared to simpler parsers.

  • showdown:

    Choose showdown only if you’re maintaining legacy code that already depends on it or if you need basic Markdown support with minimal dependencies. Its regex-based engine is less reliable with edge cases and nested structures, and it lacks the modern extensibility models of other libraries. For new projects, prefer marked or markdown-it instead.

  • micromark:

    Choose micromark when performance, spec compliance, and composability are top priorities. As a low-level streaming parser, it’s perfect for building custom tooling, static site generators, or high-throughput APIs where you need to process Markdown efficiently. However, you’ll need additional utilities (like mdast-util-from-markdown) if you want to work with syntax trees or perform transformations.

README for marked

Marked

npm install size downloads github actions snyk

  • ⚡ built for speed
  • ⬇️ low-level compiler for parsing markdown without caching or blocking for long periods of time
  • ⚖️ light-weight while implementing all markdown features from the supported flavors & specifications
  • 🌐 works in a browser, on a server, or from a command line interface (CLI)

Demo

Check out the demo page to see Marked in action ⛹️

Docs

Our documentation pages are also rendered using marked 💯

Also read about:

Compatibility

Node.js: Only current and LTS Node.js versions are supported. End of life Node.js versions may become incompatible with Marked at any point in time.

Browser: Baseline Widely Available

Installation

CLI:

npm install -g marked

In-browser:

npm install marked

Usage

Warning: 🚨 Marked does not sanitize the output HTML. Please use a sanitize library, like DOMPurify (recommended), sanitize-html or insane on the output HTML! 🚨

DOMPurify.sanitize(marked.parse(`<img src="x" onerror="alert('not happening')">`));

CLI

# Example with stdin input
$ marked -o hello.html
hello world
^D
$ cat hello.html
<p>hello world</p>
# Print all options
$ marked --help

Browser

<!doctype html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>Marked in the browser</title>
</head>
<body>
  <div id="content"></div>
  <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
  <script>
    document.getElementById('content').innerHTML =
      marked.parse('# Marked in the browser\n\nRendered by **marked**.');
  </script>
</body>
</html>

or import esm module

<script type="module">
  import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js";
  document.getElementById('content').innerHTML =
    marked.parse('# Marked in the browser\n\nRendered by **marked**.');
</script>

License

Copyright (c) 2018+, MarkedJS. (MIT License) Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)