---
title: "Markdown-it"
description: "Use existing markdown-it plugins with Comark and create new parser syntax using markdown-it rules."
canonical_url: "https://comark.dev/plugins/custom/markdown-it"
---
# Markdown-it

> Use existing markdown-it plugins with Comark and create new parser syntax using markdown-it rules.

Comark's default parser uses [markdown-exit](https://github.com/serkodev/markdown-exit), a TypeScript rewrite of [markdown-it](https://github.com/markdown-it/markdown-it) that preserves its plugin API. Most markdown-it plugins work with Comark with minimal effort.

## Usage

Wrap any markdown-it plugin with [`defineComarkPlugin`](https://comark.dev/plugins/custom/plugin-api) and pass it in `markdownItPlugins`:

```typescript
import { defineComarkPlugin } from 'comark/parse'
import markdownItSub from 'markdown-it-sub'
import markdownItSup from 'markdown-it-sup'

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

const superscript = defineComarkPlugin(() => ({
  name: 'superscript',
  markdownItPlugins: [markdownItSup],
}))
```

<code-group>
```typescript [Parse API]
import { parseMarkdown } from 'comark'

const tree = await parseMarkdown('H~2~O is water, 2^10^ is 1024', {
  plugins: [subscript(), superscript()]
})
```


```vue [Vue]
<script setup lang="ts">
import { Markdown } from '@comark/vue'

const plugins = [subscript(), superscript()]
</script>

<template>
  <Markdown :plugins="plugins">H~2~O is water</Markdown>
</template>
```
</code-group>

## Create

To add new syntax, write a markdown-it inline or block rule and include it in `markdownItPlugins`. Comark automatically converts the resulting tokens into AST nodes.

The following example adds `==highlighted text==` syntax:

```typescript
import type { MarkdownItPlugin } from 'comark'
import { defineComarkPlugin } from 'comark/parse'

const highlightRule = (state: any, silent: boolean) => {
  const start = state.pos
  const max = state.posMax

  if (start + 1 >= max) return false
  if (state.src.charCodeAt(start) !== 0x3D /* = */) return false
  if (state.src.charCodeAt(start + 1) !== 0x3D /* = */) return false

  let pos = start + 2
  while (pos + 1 < max) {
    if (state.src.charCodeAt(pos) === 0x3D && state.src.charCodeAt(pos + 1) === 0x3D) {
      if (!silent) {
        const token = state.push('mark_open', 'mark', 1)
        token.markup = '=='

        state.pos = start + 2
        state.posMax = pos
        state.md.inline.tokenize(state)

        state.push('mark_close', 'mark', -1)
        state.posMax = max
      }
      state.pos = pos + 2
      return true
    }
    pos++
  }

  return false
}

const markdownItHighlight: MarkdownItPlugin = (md) => {
  md.inline.ruler.before('emphasis', 'mark', highlightRule)
}

export default defineComarkPlugin(() => ({
  name: 'highlight',
  markdownItPlugins: [markdownItHighlight],
}))
```

`==hello==` becomes the following AST node:

```json
["mark", {}, "hello"]
```

## Compatibility

When you provide `markdownItPlugins`, they are registered directly on the underlying markdown-it instance with full access to its API:

- **Inline rules**: [`md.inline.ruler`](https://markdown-it.github.io/markdown-it/#Ruler)
- **Block rules**: [`md.block.ruler`](https://markdown-it.github.io/markdown-it/#Ruler)
- **Core rules**: [`md.core.ruler`](https://markdown-it.github.io/markdown-it/#Ruler)
- **Renderer rules**: [`md.renderer.rules`](https://markdown-it.github.io/markdown-it/#Renderer) (limited, see below)

### What works

Most plugins that add **parsing rules** work out of the box:

- Plugins that add new inline syntax (subscript, superscript, mark, insert)
- Plugins that add new block syntax (containers, definition lists, footnotes)
- Plugins that transform tokens via core rules (abbreviations, replacements)

### What doesn't work

<warning>
Plugins that rely on markdown-it's **renderer** will not work as expected. Comark uses its own rendering pipeline instead of `md.renderer`, so any logic in `md.renderer.rules` is ignored.
</warning>

If a plugin only customizes rendering, handle the rendering side in Comark using custom components:

<code-group>
```vue [Vue]
<script setup lang="ts">
import { Markdown } from '@comark/vue'

const components = {
  mark: (props, { slots }) => h('mark', { class: 'highlight' }, slots.default?.())
}
</script>

<template>
  <Markdown :components="components">==highlighted text==</Markdown>
</template>
```


```tsx [React]
import { Markdown } from '@comark/react'

const components = {
  mark: ({ children, ...props }) => <mark className="highlight" {...props}>{children}</mark>
}

<Markdown components={components}>{content}</Markdown>
```
</code-group>

### markdown-exit vs markdown-it

Comark uses [`markdown-exit`](https://github.com/serkodev/markdown-exit) as its base parser. This TypeScript rewrite preserves the markdown-it plugin API, so plugins written for markdown-it work without modification. The [`MarkdownItPlugin`](https://markdown-it.github.io/markdown-it/#MarkdownIt) type accepted by Comark is the standard plugin signature:

```typescript
type MarkdownItPlugin = (md: MarkdownIt) => void
```

---

- [Plugin API](https://comark.dev/plugins/custom/plugin-api)
- [Parse API](https://comark.dev/reference/parse)


## Sitemap

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