---
title: "Comark vs markdown-it"
description: "Comark is built on markdown-exit, a TypeScript rewrite of markdown-it. Compare their output, plugin compatibility, defaults, streaming, and framework rendering."
canonical_url: "https://comark.dev/compare/comark-vs-markdown-it"
---
# Comark vs markdown-it

> Comark is built on markdown-exit, a TypeScript rewrite of markdown-it. Compare their output, plugin compatibility, defaults, streaming, and framework rendering.

**TL;DR**: [markdown-it](https://github.com/markdown-it/markdown-it) parses Markdown into a flat token stream and renders it to an HTML string. Comark parses with markdown-exit, a TypeScript rewrite of markdown-it that keeps its plugin API. Comark then converts the tokens into a tree document that renders natively in Vue, React, Svelte, and Angular, and to HTML and ANSI strings.

## At a glance

|                              | Comark                                                                      | markdown-it                                                   |
| ---------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Tokenizer                    | markdown-exit, a TypeScript rewrite of markdown-it                          | markdown-it                                                   |
| Output                       | `MarkdownDocument` tree, then HTML, framework components, or ANSI           | HTML string from `md.render()`, flat tokens from `md.parse()` |
| Plugins                      | markdown-it parsing plugins, plus Comark plugins                            | markdown-it plugins with `md.use()` and the Ruler API         |
| Renderer rules               | Ignored, Comark has its own renderers                                       | `md.renderer.rules`                                           |
| Raw HTML                     | Parsed into nodes by default                                                | Off by default (`html: false`)                                |
| Linkify                      | On by default                                                               | Off by default                                                |
| Custom components in content | Yes, `::alert{type="info"}`                                                 | No, only through plugins                                      |
| Incomplete Markdown          | [`autoClose`](https://comark.dev/reference/auto-close) completes the syntax | No handling                                                   |
| API                          | Async                                                                       | Sync only                                                     |

## Same tokenizer, different output

markdown-it has two steps. `md.parse()` returns a flat list of tokens, with `_open` and `_close` pairs. `md.render()` turns those tokens into an HTML string.

Comark replaces the second step. It converts the tokens into a tree of tuples, the [document model](https://comark.dev/getting-started/document-model):

```ts
import { parseMarkdown } from 'comark'

const document = await parseMarkdown('# Hello **World**')
// document.nodes:
// [['h1', { id: 'hello-world' }, 'Hello ', ['strong', {}, 'World']]]
```

The document is JSON-safe. You can cache it, send it to a client, and render it with `<MarkdownDocument>` in [Vue](https://comark.dev/rendering/vue) or [React](https://comark.dev/rendering/react). Framework renderers create real components, not an HTML string that you insert with `innerHTML`.

## Bring your markdown-it plugins

Comark accepts markdown-it plugins through `markdownItPlugins` in a [Comark plugin](https://comark.dev/plugins/custom/plugin-api). They're registered on the underlying markdown-exit instance:

::code-group
```ts [markdown-it]
import MarkdownIt from 'markdown-it'
import markdownItSub from 'markdown-it-sub'

const md = new MarkdownIt().use(markdownItSub)
const html = md.render('H~2~O is water')
```

```ts [Comark]
import { parseMarkdown } from 'comark'
import { defineComarkPlugin } from 'comark/parse'
import markdownItSub from 'markdown-it-sub'

const subscript = defineComarkPlugin(() => ({
  name: 'subscript',
  markdownItPlugins: [markdownItSub],
}))

const document = await parseMarkdown('H~2~O is water', { plugins: [subscript()] })
```
::

Plugins that add inline rules, block rules, or core rules work, such as subscript, containers, and abbreviations. Plugins that only change `md.renderer.rules` have no effect, because Comark doesn't use the markdown-it renderer. Map the tag to a component instead. See [markdown-it plugins](https://comark.dev/plugins/custom/markdown-it) for details.

## Use Comark syntax in markdown-it

The reverse also works. If a tool already owns a markdown-it instance, such as VitePress, you can add Comark component and attribute syntax to it:

```ts
import { markdownItComponents } from 'comark/plugins/components'
import { markdownItAttributes } from 'comark/plugins/attributes'

md.use(markdownItComponents)
md.use(markdownItAttributes)
```

See the [components plugin](https://comark.dev/plugins/defaults/components) and the [VitePress example](https://comark.dev/examples/frameworks/vitepress).

## Defaults and security

markdown-it is safe by default. It disables raw HTML, and its `validateLink` rejects `javascript:`, `vbscript:`, `file:`, and non-image `data:` URLs.

Comark is open by default. Markdown link syntax with an unsafe URL doesn't become a link, but raw HTML is parsed into nodes and kept, including `<script>`. Attributes and component props are also kept as written. For untrusted input, add the [security plugin](https://comark.dev/plugins/built-in/security) with a `blockedTags` list, or set `registerDefaultPlugins: false` and register the plugins you need without `html`.

## CommonMark differences

markdown-it follows the CommonMark spec. markdown-exit keeps that behavior, but Comark's conversion to its document model changes some output. For example, `[foo]` is a Comark span instead of a shortcut reference link, and tight and loose lists render the same way. `autoClose` is on by default, so `*foo` becomes emphasis. See [Differences from CommonMark and GFM](https://comark.dev/syntax/markdown#differences-from-commonmark-and-gfm).

## What markdown-it does well

- **Safe defaults**: raw HTML is off and link validation is built in.
- **Plugin ecosystem**: a very large number of plugins on npm. Comark can use the ones that add parsing rules.
- **Maturity and speed**: markdown-it is a mature, fast parser that many tools build on, such as VitePress and Markstream.
- **Size**: Comark adds a YAML parser, an HTML parser, and its own renderers, so it's larger.

If you need a synchronous HTML string from Markdown, markdown-it is the smaller tool.

## FAQ

::accordion
  :::accordion-item{label="Do all markdown-it plugins work in Comark?"}
  No. Plugins that add parsing rules work. Plugins that rely on `md.renderer.rules` are ignored. Write a component for the tag instead. See [markdown-it plugin compatibility](https://comark.dev/plugins/custom/markdown-it).
  :::

  :::accordion-item{label="Is markdown-exit a fork of markdown-it?"}
  It's a TypeScript rewrite that keeps the markdown-it plugin API. Plugins written for markdown-it run without changes when they only add parsing rules.
  :::

  :::accordion-item{label="Can I get markdown-it HTML output from Comark?"}
  Use [`renderHtml()`](https://comark.dev/rendering/html) from `@comark/html`. The HTML is close to markdown-it output for standard Markdown, with the documented differences, such as heading IDs and `language` attributes on code blocks.
  :::

  :::accordion-item{label="How do I stream LLM output with markdown-it?"}
  markdown-it doesn't handle incomplete Markdown. Comark closes unterminated syntax on every parse and its renderers have a `streaming` prop. See [AI chat streaming](https://comark.dev/use-cases/ai-chat-streaming).
  :::
::

---

- [markdown-it plugins](https://comark.dev/plugins/custom/markdown-it)
- [Playground](https://comark.dev/play)


## Sitemap

See the full [sitemap](https://comark.dev/sitemap.md) for all pages.
