Markdown

Comark parses CommonMark and GitHub Flavored Markdown (GFM), including headings, formatting, lists, tables, and code blocks, with a few documented differences.

Comark parses CommonMark and GitHub Flavored Markdown (GFM). The parser is built on markdown-exit, which follows the CommonMark spec. Comark then converts the tokens into its own document model, and a few constructs render differently. See Differences from CommonMark and GFM.

Headings

# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6

All headings automatically get ID attributes generated from their content for linking:

# Hello World
<!-- Becomes: <h1 id="hello-world">Hello World</h1> -->

Text formatting

Bold text
Italic text
Bold and italic
Strikethrough
Inline code
**Bold text**
*Italic text*
***Bold and italic***
~~Strikethrough~~
`Inline code`
Text nodes in Comark are plain strings, similar to HTML text nodes. To add custom styles, attributes, or metadata to specific parts of text, use the Span Attributes syntax: Hello [world]{data-world="earth" style="color: blue"}.

This creates a <span> element with custom attributes, so you can style or add metadata to inline text.

Lists

Unordered

- Item 1
- Item 2
  - Nested item
  - Another nested item
- Item 3

Ordered

1. First item
2. Second item
   1. Nested item
   2. Another nested item
3. Third item
[Link text](https://example.com)
[Link with title](https://example.com "Link title")
![Image alt text](https://example.com/image.png)
![Image with title](https://example.com/image.png "Image title")
Add custom attributes to links and images with the Attributes syntax: [Link](url){target="_blank"}.

Bare URLs like https://example.com are automatically converted into clickable links. Disable this with the linkify option ({ linkify: false }).

Blockquotes

This is a blockquote

And contain other markdown elements like bold and italic

> This is a blockquote
>
> And contain other markdown elements like **bold** and *italic*

Alerts

The alerts plugin is built-in and transforms special blockquotes into styled callout blocks. Place an alert marker on the first line of a blockquote:

> [!NOTE]
> Useful information that users should know, even when skimming content.

> [!TIP]
> Helpful advice for doing things better or more easily.

> [!IMPORTANT]
> Key information users need to know to achieve their goal.

> [!WARNING]
> Urgent info that needs immediate user attention to avoid problems.

> [!CAUTION]
> Advises about risks or negative outcomes of certain actions.

Supported markers: [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION].

Horizontal rules

Any of these create a horizontal rule:

---
***
___

Code blocks

Comark provides advanced code block features with metadata support.

Basic code block

function hello() {
  console.log("Hello, World!")
}
```javascript
function hello() {
  console.log("Hello, World!")
}
```

Filename metadata

Add a filename using [...] brackets:

server.js
const express = require('express')
const app = express()
```javascript [server.js]
const express = require('express')
const app = express()
```

Line highlighting

Highlight specific lines using {...} syntax:

function example() {
  const a = 1
  const b = 2
  const c = 3
  return a + b + c
}
```javascript {1-3,5}
function example() {
  const a = 1
  const b = 2
  const c = 3
  return a + b + c
}
```
SyntaxDescription
{3}Single line
{1-5}Range of lines
{1,3,5}Multiple specific lines
{1-3,7,10-12}Combined ranges and lines

Combined metadata

All metadata can be combined in any order:

utils.ts
function hello() {
  console.log("Hello")
}
```javascript {1-3} [utils.ts] meta=value
function hello() {
  console.log("Hello")
}
```

Special characters in filename

Use backslash to escape special characters:

@[...slug\].ts
// Brackets and special chars are supported
```typescript [@[...slug\].ts]
// Brackets and special chars are supported
```

AST structure

Code blocks produce this AST structure:

[
  "pre",
  {
    "language": "javascript",
    "filename": "server.js",
    "highlights": [1, 2, 3],
    "meta": "meta=value"
  },
  ["code", { "class": "language-javascript" }, "code content here"]
]

Task lists

Comark supports GitHub Flavored Markdown task lists:

  • Completed task
  • Pending task
  • Another completed task

    • Nested pending task
    • Nested completed task
- [x] Completed task
- [ ] Pending task
- [x] Another completed task
  - [ ] Nested pending task
  - [x] Nested completed task
  • [x] or [X] for completed tasks
  • [ ] for pending tasks
  • Works in both ordered and unordered lists
  • Supports nesting

Tables

Header 1Header 2Header 3
Cell 1Cell 2Cell 3
Cell 4Cell 5Cell 6
| Header 1 | Header 2 | Header 3 |
| -------- | -------- | -------- |
| Cell 1   | Cell 2   | Cell 3   |
| Cell 4   | Cell 5   | Cell 6   |

Aligned tables

Left AlignedCenter AlignedRight Aligned
LeftCenterRight
TextTextText
| Left Aligned | Center Aligned | Right Aligned |
| :----------- | :------------: | ------------: |
| Left         | Center         | Right         |
| Text         | Text           | Text          |
SyntaxAlignment
:---Left
:---:Center
---:Right

Inline Markdown in tables

FeatureStatusLink
BoldItalicLink
CodeStrike
| Feature      | Status          | Link                    |
| ------------ | --------------- | ----------------------- |
| **Bold**     | *Italic*        | [Link](https://example) |
| `Code`       | ~~Strike~~      | ![Image](https://picsum.photos/120/30)       |

Comments

HTML-style comments are supported and preserved in the AST but not rendered in output:

<!-- This is a comment -->

Comments can span multiple lines:

<!--
This is a multi-line comment
that can contain any text
-->

Comments are represented in the document model as a tuple with null as the tag:

[null, {}, " comment text "]

Emojis

Emoji shortcodes use the :emoji_name: syntax and require the emoji plugin:

Hello πŸ‘‹ Welcome to our docs! πŸš€

Hello :wave: Welcome to our docs! :rocket:

πŸ˜„ ❀️ πŸ”₯ πŸš€ ✨ πŸŽ‰ πŸ€” πŸ‘€ πŸ’― ⭐ ⚑ πŸ’‘ ⚠️

:smile: :heart: :fire: :rocket: :sparkles: :tada:
:thinking: :eyes: :100: :star: :zap: :bulb: :warning:

Differences from CommonMark and GFM

Comark renders most CommonMark and GFM documents the same way as a spec-compliant parser. The differences below come from Comark syntax, from the default options, or from the conversion to the document model.

Comark syntax

  • Shortcut reference links. [text] without a destination is a span, so [foo] doesn't resolve a [foo]: /url definition. Full ([foo][ref]) and collapsed ([foo][]) reference links work.
  • Component and attribute markers. Text such as ::name, :name[...], and {...} after an element is Comark syntax. Set registerDefaultPlugins: false to parse it as plain text.

Default options

  • autoClose is on by default, so unterminated syntax is closed: *foo bar renders as emphasis, and an unclosed ` becomes inline code. Set autoClose: false for static content that must follow the spec. See the Streaming API.
  • autoUnwrap removes the <p> inside a list item or a blockquote that holds a single paragraph.
  • headingIds adds an id to every heading.

Document model

  • Tight and loose lists aren't distinguished. List items render the same way with or without blank lines between them.
  • Empty link text removes the link: [](/url) renders nothing.
  • Image alt text keeps the raw Markdown: ![foo *bar*](/img.png) gives the alt text foo *bar*.
  • Markdown inside an HTML block, such as a <div> followed by a blank line, renders after the HTML element instead of inside it.
  • Fenced code blocks add language and meta attributes to the <pre> element.

GFM extensions

  • Email autolinks such as foo@bar.baz stay plain text. URL autolinks such as www.example.com work.
  • The tag filter isn't applied: raw <title>, <style>, <xmp> and the other filtered tags aren't escaped. Set blockedTags in the security plugin to remove them.
Β© 2026 Vercel, Inc.