---
title: "Markdown"
description: "Comark parses CommonMark and GitHub Flavored Markdown (GFM), including headings, formatting, lists, tables, and code blocks, with a few documented differences."
canonical_url: "https://comark.dev/syntax/markdown"
---
# 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](https://comark.dev/getting-started/document-model), and a few constructs render differently. See [Differences from CommonMark and GFM](#differences-from-commonmark-and-gfm).

## Headings

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

All headings automatically get an `id` from their visible text, for linking. Set [`headingIds: false`](https://comark.dev/reference/parse#options) to skip generation. An explicit `{id="..."}` replaces the generated id.

```mdc
# Hello World
<!-- <h1 id="hello-world">Hello World</h1> -->

## 1. Introduction
<!-- <h2 id="_1-introduction">1. Introduction</h2> -->

## The `parse` function
<!-- <h2 id="the-parse-function">The parse function</h2> -->
```

The id is the heading's visible text, lowercased, with spaces turned into `-` and other punctuation removed. A leading digit is prefixed with `_`. `**bold**`, ```code```, and link labels contribute their text, not the tag name. An inline component name is omitted (`## Star :icon-star here` → `star-here`).

Ids are unique within a document. A heading whose id is already taken gets a numeric suffix, at any level and in any section:

```md
## Npm
### Install
<!-- id="install" -->

## Yarn
### Install
<!-- id="install-1" -->

## Options
## Options
<!-- id="options", then "options-1" -->
```

Ids are flat by default. Set `headingIds: 'nested'` in the [parse options](https://comark.dev/reference/parse#options) to prefix each id from `h3` down with the nearest `h2` or deeper ancestor (never the `h1`):

```md
# Guide

## Ending space

### Slash in title
<!-- headingIds: 'nested' → id="ending-space-slash-in-title" -->
```

## Text formatting

::code-preview
  :::div
  **Bold text**   

  *Italic text*   

  ***Bold and italic***   

  ~~Strikethrough~~   

  `Inline code`
  :::

#code
```mdc
**Bold text**
*Italic text*
***Bold and italic***
~~Strikethrough~~
`Inline code`
```
::

::callout
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](https://comark.dev/syntax/attributes#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

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

### Ordered

```mdc
1. First item
2. Second item
   1. Nested item
   2. Another nested item
3. Third item
```

## Links and images

```mdc
[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")
```

::callout{icon="i-lucide-info"}
Add custom attributes to links and images with the [Attributes](https://comark.dev/syntax/attributes) syntax: `[Link](url){target="_blank"}`.
::

Bare URLs like `https://example.com` are automatically converted into clickable links. Disable this with the [`linkify`](https://comark.dev/reference/parse#options) option (`{ linkify: false }`).

## Blockquotes

::code-preview
  :::div
  > This is a blockquote
  >
  > And contain other markdown elements like **bold** and *italic*
  :::

#code
```mdc
> This is a blockquote
>
> And contain other markdown elements like **bold** and *italic*
```
::

### Alerts

The [alerts plugin](https://comark.dev/plugins/defaults/alert) is built-in and transforms special blockquotes into styled callout blocks. Place an alert marker on the first line of a blockquote:

```mdc
> [!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:

```mdc
---
***
___
```

---

## Code blocks

Comark provides advanced code block features with metadata support.

### Basic code block

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

#code
~~~mdc
```javascript
function hello() {
  console.log("Hello, World!")
}
```
~~~
::

### Filename metadata

Add a filename using `[...]` brackets:

::code-preview
```javascript [server.js]
const express = require('express')
const app = express()
```

#code
~~~mdc
```javascript [server.js]
const express = require('express')
const app = express()
```
~~~
::

### Line highlighting

Highlight specific lines using `{...}` syntax:

::code-preview
```javascript {1-3,5}
function example() {
  const a = 1
  const b = 2
  const c = 3
  return a + b + c
}
```

#code
~~~mdc
```javascript {1-3,5}
function example() {
  const a = 1
  const b = 2
  const c = 3
  return a + b + c
}
```
~~~
::

| Syntax          | Description               |
| --------------- | ------------------------- |
| `{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:

::code-preview
```javascript [utils.ts] {1-3}
function hello() {
  console.log("Hello")
}
```

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

### Special characters in filename

Use backslash to escape special characters:

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

#code
~~~mdc
```typescript [@[...slug\].ts]
// Brackets and special chars are supported
```
~~~
::

### AST structure

Code blocks produce this AST structure:

```json
[
  "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:

::code-preview
- [x] Completed task
- [ ] Pending task
- [ ] - [ ] Nested pending task
  - [x] Nested completed task

#code
```mdc
- [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

::code-preview
| Header 1 | Header 2 | Header 3 |
| -------- | -------- | -------- |
| Cell 1   | Cell 2   | Cell 3   |
| Cell 4   | Cell 5   | Cell 6   |

#code
```mdc
| Header 1 | Header 2 | Header 3 |
| -------- | -------- | -------- |
| Cell 1   | Cell 2   | Cell 3   |
| Cell 4   | Cell 5   | Cell 6   |
```
::

### Aligned tables

::code-preview
| Left Aligned | Center Aligned | Right Aligned |
| :----------- | :------------: | ------------: |
| Left         | Center         | Right         |
| Text         | Text           | Text          |

#code
```mdc
| Left Aligned | Center Aligned | Right Aligned |
| :----------- | :------------: | ------------: |
| Left         | Center         | Right         |
| Text         | Text           | Text          |
```
::

| Syntax  | Alignment |
| ------- | --------- |
| `:---`  | Left      |
| `:---:` | Center    |
| `---:`  | Right     |

### Inline Markdown in tables

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

#code
```mdc
| 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:

```mdc
<!-- This is a comment -->
```

Comments can span multiple lines:

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

Comments are represented in the [document model](https://comark.dev/getting-started/document-model#comment-nodes) as a tuple with `null` as the tag:

```json
[null, {}, " comment text "]
```

## Emojis

Emoji shortcodes use the `:emoji_name:` syntax and require the [emoji plugin](https://comark.dev/plugins/built-in/emoji):

::code-preview
Hello 👋 Welcome to our docs! 🚀

#code
```mdc
Hello :wave: Welcome to our docs! :rocket:
```
::

### Popular emojis

::code-preview
😄 ❤️ 🔥 🚀 ✨ 🎉 🤔 👀 💯 ⭐ ⚡ 💡 ⚠️

#code
```mdc
: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](https://comark.dev/syntax/attributes#span-attributes), 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](https://comark.dev/reference/auto-close).
- **`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](https://comark.dev/plugins/built-in/security) to remove them.

---

- [Frontmatter](https://comark.dev/syntax/frontmatter)
- [Components](https://comark.dev/syntax/components)


## Sitemap

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