Skip to content

Markdown Extensions

VitePress comes with built in Markdown Extensions.

Header Anchors

Headers automatically get anchor links applied. Rendering of anchors can be configured using the markdown.anchor option.

Custom anchors

To specify a custom anchor tag for a heading instead of using the auto-generated one, add a suffix to the heading:

# Using custom anchors {#my-anchor}
# Using custom anchors {#my-anchor}

This allows you to link to the heading as #my-anchor instead of the default #using-custom-anchors.

Both internal and external links gets special treatments.

Internal links are converted to router link for SPA navigation. Also, every index.md contained in each sub-directory will automatically be converted to index.html, with corresponding URL /.

For example, given the following directory structure:

.
├─ index.md
├─ foo
│  ├─ index.md
│  ├─ one.md
│  └─ two.md
└─ bar
   ├─ index.md
   ├─ three.md
   └─ four.md
.
├─ index.md
├─ foo
│  ├─ index.md
│  ├─ one.md
│  └─ two.md
└─ bar
   ├─ index.md
   ├─ three.md
   └─ four.md

And providing you are in foo/one.md:

md
[Home](/) <!-- sends the user to the root index.md -->
[foo](/foo/) <!-- sends the user to index.html of directory foo -->
[foo heading](./#heading) <!-- anchors user to a heading in the foo index file -->
[bar - three](../bar/three) <!-- you can omit extension -->
[bar - three](../bar/three.md) <!-- you can append .md -->
[bar - four](../bar/four.html) <!-- or you can append .html -->
[Home](/) <!-- sends the user to the root index.md -->
[foo](/foo/) <!-- sends the user to index.html of directory foo -->
[foo heading](./#heading) <!-- anchors user to a heading in the foo index file -->
[bar - three](../bar/three) <!-- you can omit extension -->
[bar - three](../bar/three.md) <!-- you can append .md -->
[bar - four](../bar/four.html) <!-- or you can append .html -->

Page Suffix

Pages and internal links get generated with the .html suffix by default.

Outbound links automatically get target="_blank" rel="noreferrer":

Frontmatter

YAML frontmatter is supported out of the box:

yaml
---
title: Blogging Like a Hacker
lang: en-US
---
---
title: Blogging Like a Hacker
lang: en-US
---

This data will be available to the rest of the page, along with all custom and theming components.

For more details, see Frontmatter.

GitHub-Style Tables

Input

| Tables        |      Are      |  Cool |
| ------------- | :-----------: | ----: |
| col 3 is      | right-aligned | $1600 |
| col 2 is      |   centered    |   $12 |
| zebra stripes |   are neat    |    $1 |
| Tables        |      Are      |  Cool |
| ------------- | :-----------: | ----: |
| col 3 is      | right-aligned | $1600 |
| col 2 is      |   centered    |   $12 |
| zebra stripes |   are neat    |    $1 |

Output

TablesAreCool
col 3 isright-aligned$1600
col 2 iscentered$12
zebra stripesare neat$1

Emoji 🎉

Input

:tada: :100:
:tada: :100:

Output

🎉 💯

A list of all emojis is available.

Table of Contents

Input

[[toc]]
[[toc]]

Output

Rendering of the TOC can be configured using the markdown.toc option.

Custom Containers

Custom containers can be defined by their types, titles, and contents.

Default Title

Input

md
::: info
This is an info box.
:::

::: tip
This is a tip.
:::

::: warning
This is a warning.
:::

::: danger
This is a dangerous warning.
:::

::: details
This is a details block.
:::
::: info
This is an info box.
:::

::: tip
This is a tip.
:::

::: warning
This is a warning.
:::

::: danger
This is a dangerous warning.
:::

::: details
This is a details block.
:::

Output

INFO

This is an info box.

TIP

This is a tip.

WARNING

This is a warning.

DANGER

This is a dangerous warning.

Details

This is a details block.

Custom Title

You may set custom title by appending the text right after the "type" of the container.

Input

md
::: danger STOP
Danger zone, do not proceed
:::

::: details Click me to view the code
```js
console.log('Hello, VitePress!')
```
:::
::: danger STOP
Danger zone, do not proceed
:::

::: details Click me to view the code
```js
console.log('Hello, VitePress!')
```
:::

Output

STOP

Danger zone, do not proceed

Click me to view the code
js
console.log('Hello, VitePress!')
console.log('Hello, VitePress!')

Also, you may set custom titles globally by adding the following content in site config, helpful if not writing in English:

ts
// config.ts
export default defineConfig({
  // ...
  markdown: {
    container: {
      tipLabel: '提示',
      warningLabel: '警告',
      dangerLabel: '危险',
      infoLabel: '信息',
      detailsLabel: '详细信息'
    }
  }
  // ...
})
// config.ts
export default defineConfig({
  // ...
  markdown: {
    container: {
      tipLabel: '提示',
      warningLabel: '警告',
      dangerLabel: '危险',
      infoLabel: '信息',
      detailsLabel: '详细信息'
    }
  }
  // ...
})

raw

This is a special container that can be used to prevent style and router conflicts with VitePress. This is especially useful when you're documenting component libraries. You might also wanna check out whyframe for better isolation.

Syntax

md
::: raw
Wraps in a <div class="vp-raw">
:::
::: raw
Wraps in a <div class="vp-raw">
:::

vp-raw class can be directly used on elements too. Style isolation is currently opt-in:

  • Install postcss with your preferred package manager:

    sh
    $ npm add -D postcss
    $ npm add -D postcss
  • Create a file named docs/postcss.config.mjs and add this to it:

    js
    import { postcssIsolateStyles } from 'vitepress'
    
    export default {
      plugins: [postcssIsolateStyles()]
    }
    import { postcssIsolateStyles } from 'vitepress'
    
    export default {
      plugins: [postcssIsolateStyles()]
    }

    It uses postcss-prefix-selector under the hood. You can pass its options like this:

    js
    postcssIsolateStyles({
      includeFiles: [/vp-doc\.css/] // defaults to /base\.css/
    })
    postcssIsolateStyles({
      includeFiles: [/vp-doc\.css/] // defaults to /base\.css/
    })

Syntax Highlighting in Code Blocks

VitePress uses Shiki to highlight language syntax in Markdown code blocks, using coloured text. Shiki supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block:

Input

```js
export default {
  name: 'MyComponent',
  // ...
}
```
```js
export default {
  name: 'MyComponent',
  // ...
}
```
```html
<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
  </li>
</ul>
```
```html
<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
  </li>
</ul>
```

Output

js
export default {
  name: 'MyComponent'
  // ...
}
export default {
  name: 'MyComponent'
  // ...
}
html
<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
  </li>
</ul>
<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
  </li>
</ul>

A list of valid languages is available on Shiki's repository.

You may also customize syntax highlight theme in app config. Please see markdown options for more details.

Line Highlighting in Code Blocks

Input

```js{4}
export default {
  data () {
    return {
      msg: 'Highlighted!'
    }
  }
}
```
```js{4}
export default {
  data () {
    return {
      msg: 'Highlighted!'
    }
  }
}
```

Output

js
export default {
  data () {
    return {
      msg: 'Highlighted!'
    }
  }
}
export default {
  data () {
    return {
      msg: 'Highlighted!'
    }
  }
}

In addition to a single line, you can also specify multiple single lines, ranges, or both:

  • Line ranges: for example {5-8}, {3-10}, {10-17}
  • Multiple single lines: for example {4,7,9}
  • Line ranges and single lines: for example {4,7-13,16,23-27,40}

Input

```js{1,4,6-8}
export default { // Highlighted
  data () {
    return {
      msg: `Highlighted!
      This line isn't highlighted,
      but this and the next 2 are.`,
      motd: 'VitePress is awesome',
      lorem: 'ipsum'
    }
  }
}
```
```js{1,4,6-8}
export default { // Highlighted
  data () {
    return {
      msg: `Highlighted!
      This line isn't highlighted,
      but this and the next 2 are.`,
      motd: 'VitePress is awesome',
      lorem: 'ipsum'
    }
  }
}
```

Output

js
export default { // Highlighted
  data () {
    return {
      msg: `Highlighted!
      This line isn't highlighted,
      but this and the next 2 are.`,
      motd: 'VitePress is awesome',
      lorem: 'ipsum',
    }
  }
}
export default { // Highlighted
  data () {
    return {
      msg: `Highlighted!
      This line isn't highlighted,
      but this and the next 2 are.`,
      motd: 'VitePress is awesome',
      lorem: 'ipsum',
    }
  }
}

Alternatively, it's possible to highlight directly in the line by using the // [!code hl] comment.

Input

```js
export default {
  data () {
    return {
      msg: 'Highlighted!' // [!code  hl]
    }
  }
}
```
```js
export default {
  data () {
    return {
      msg: 'Highlighted!' // [!code  hl]
    }
  }
}
```

Output

js
export default {
  data() {
    return {
      msg: 'Highlighted!' 
    }
  }
}
export default {
  data() {
    return {
      msg: 'Highlighted!' 
    }
  }
}

Focus in Code Blocks

Adding the // [!code focus] comment on a line will focus it and blur the other parts of the code.

Additionally, you can define a number of lines to focus using // [!code focus:<lines>].

Input

Note that only one space is required after !code, here are two to prevent processing.

```js
export default {
  data () {
    return {
      msg: 'Focused!' // [!code  focus]
    }
  }
}
```
```js
export default {
  data () {
    return {
      msg: 'Focused!' // [!code  focus]
    }
  }
}
```

Output

js
export default {
  data() {
    return {
      msg: 'Focused!' 
    }
  }
}
export default {
  data() {
    return {
      msg: 'Focused!' 
    }
  }
}

Colored Diffs in Code Blocks

Adding the // [!code --] or // [!code ++] comments on a line will create a diff of that line, while keeping the colors of the codeblock.

Input

Note that only one space is required after !code, here are two to prevent processing.

```js
export default {
  data () {
    return {
      msg: 'Removed' // [!code  --]
      msg: 'Added' // [!code  ++]
    }
  }
}
```
```js
export default {
  data () {
    return {
      msg: 'Removed' // [!code  --]
      msg: 'Added' // [!code  ++]
    }
  }
}
```

Output

js
export default {
  data () {
    return {
      msg: 'Removed' 
      msg: 'Added' 
    }
  }
}
export default {
  data () {
    return {
      msg: 'Removed' 
      msg: 'Added' 
    }
  }
}

Errors and Warnings in Code Blocks

Adding the // [!code warning] or // [!code error] comments on a line will color it accordingly.

Input

Note that only one space is required after !code, here are two to prevent processing.

```js
export default {
  data () {
    return {
      msg: 'Error', // [!code  error]
      msg: 'Warning' // [!code  warning]
    }
  }
}
```
```js
export default {
  data () {
    return {
      msg: 'Error', // [!code  error]
      msg: 'Warning' // [!code  warning]
    }
  }
}
```

Output

js
export default {
  data() {
    return {
      msg: 'Error', 
      msg: 'Warning' 
    }
  }
}
export default {
  data() {
    return {
      msg: 'Error', 
      msg: 'Warning' 
    }
  }
}

Line Numbers

You can enable line numbers for each code blocks via config:

js
export default {
  markdown: {
    lineNumbers: true
  }
}
export default {
  markdown: {
    lineNumbers: true
  }
}

Please see markdown options for more details.

You can add :line-numbers / :no-line-numbers mark in your fenced code blocks to override the value set in config.

You can also customize the starting line number by adding = after :line-numbers. For example, :line-numbers=2 means the line numbers in code blocks will start from 2.

Input

md
```ts {1}
// line-numbers is disabled by default
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```

```ts:line-numbers {1}
// line-numbers is enabled
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```

```ts:line-numbers=2 {1}
// line-numbers is enabled and start from 2
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
```ts {1}
// line-numbers is disabled by default
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```

```ts:line-numbers {1}
// line-numbers is enabled
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```

```ts:line-numbers=2 {1}
// line-numbers is enabled and start from 2
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```

Output

ts
// line-numbers is disabled by default
const line2 = 'This is line 2'
const line3 = 'This is line 3'
// line-numbers is disabled by default
const line2 = 'This is line 2'
const line3 = 'This is line 3'
ts
// line-numbers is enabled
const line2 = 'This is line 2'
const line3 = 'This is line 3'
// line-numbers is enabled
const line2 = 'This is line 2'
const line3 = 'This is line 3'
ts
// line-numbers is enabled and start from 2
const line3 = 'This is line 3'
const line4 = 'This is line 4'
// line-numbers is enabled and start from 2
const line3 = 'This is line 3'
const line4 = 'This is line 4'

Import Code Snippets

You can import code snippets from existing files via following syntax:

md
<<< @/filepath
<<< @/filepath

It also supports line highlighting:

md
<<< @/filepath{highlightLines}
<<< @/filepath{highlightLines}

Input

md
<<< @/snippets/snippet.js{2}
<<< @/snippets/snippet.js{2}

Code file

js
export default function () {
  // ..
}
export default function () {
  // ..
}

Output

js
export default function () {
  // ..
}
export default function () {
  // ..
}

TIP

The value of @ corresponds to the source root. By default it's the VitePress project root, unless srcDir is configured. Alternatively, you can also import from relative paths:

md
<<< ../snippets/snippet.js
<<< ../snippets/snippet.js

You can also use a VS Code region to only include the corresponding part of the code file. You can provide a custom region name after a # following the filepath:

Input

md
<<< @/snippets/snippet-with-region.js#snippet{1}
<<< @/snippets/snippet-with-region.js#snippet{1}

Code file

js
// #region snippet
function foo() {
  // ..
}
// #endregion snippet

export default foo
// #region snippet
function foo() {
  // ..
}
// #endregion snippet

export default foo

Output

js
function foo() {
  // ..
}
function foo() {
  // ..
}

You can also specify the language inside the braces ({}) like this:

md
<<< @/snippets/snippet.cs{c#}

<!-- with line highlighting: -->

<<< @/snippets/snippet.cs{1,2,4-6 c#}

<!-- with line numbers: -->

<<< @/snippets/snippet.cs{1,2,4-6 c#:line-numbers}
<<< @/snippets/snippet.cs{c#}

<!-- with line highlighting: -->

<<< @/snippets/snippet.cs{1,2,4-6 c#}

<!-- with line numbers: -->

<<< @/snippets/snippet.cs{1,2,4-6 c#:line-numbers}

This is helpful if source language cannot be inferred from your file extension.

Code Groups

You can group multiple code blocks like this:

Input

md
::: code-group

```js [config.js]
/**
 * @type {import('vitepress').UserConfig}
 */
const config = {
  // ...
}

export default config
```

```ts [config.ts]
import type { UserConfig } from 'vitepress'

const config: UserConfig = {
  // ...
}

export default config
```

:::
::: code-group

```js [config.js]
/**
 * @type {import('vitepress').UserConfig}
 */
const config = {
  // ...
}

export default config
```

```ts [config.ts]
import type { UserConfig } from 'vitepress'

const config: UserConfig = {
  // ...
}

export default config
```

:::

Output

js
/**
 * @type {import('vitepress').UserConfig}
 */
const config = {
  // ...
}

export default config
/**
 * @type {import('vitepress').UserConfig}
 */
const config = {
  // ...
}

export default config
ts
import type { UserConfig } from 'vitepress'

const config: UserConfig = {
  // ...
}

export default config
import type { UserConfig } from 'vitepress'

const config: UserConfig = {
  // ...
}

export default config

You can also import snippets in code groups:

Input

md
::: code-group

<!-- filename is used as title by default -->

<<< @/snippets/snippet.js

<!-- you can provide a custom one too -->

<<< @/snippets/snippet-with-region.js#snippet{1,2 ts:line-numbers} [snippet with region]

:::
::: code-group

<!-- filename is used as title by default -->

<<< @/snippets/snippet.js

<!-- you can provide a custom one too -->

<<< @/snippets/snippet-with-region.js#snippet{1,2 ts:line-numbers} [snippet with region]

:::

Output

js
export default function () {
  // ..
}
export default function () {
  // ..
}
ts
function foo() {
  // ..
}
function foo() {
  // ..
}

Markdown File Inclusion

You can include a markdown file in another markdown file, even nested.

TIP

You can also prefix the markdown path with @, it will act as the source root. By default, it's the VitePress project root, unless srcDir is configured.

For example, you can include a relative markdown file using this:

Input

md
# Docs

## Basics

<!--@include: ./parts/basics.md-->
# Docs

## Basics

<!--@include: ./parts/basics.md-->

Part file (parts/basics.md)

md
Some getting started stuff.

### Configuration

Can be created using `.foorc.json`.
Some getting started stuff.

### Configuration

Can be created using `.foorc.json`.

Equivalent code

md
# Docs

## Basics

Some getting started stuff.

### Configuration

Can be created using `.foorc.json`.
# Docs

## Basics

Some getting started stuff.

### Configuration

Can be created using `.foorc.json`.

It also supports selecting a line range:

Input

md
# Docs

## Basics

<!--@include: ./parts/basics.md{3,}-->
# Docs

## Basics

<!--@include: ./parts/basics.md{3,}-->

Part file (parts/basics.md)

md
Some getting started stuff.

### Configuration

Can be created using `.foorc.json`.
Some getting started stuff.

### Configuration

Can be created using `.foorc.json`.

Equivalent code

md
# Docs

## Basics

### Configuration

Can be created using `.foorc.json`.
# Docs

## Basics

### Configuration

Can be created using `.foorc.json`.

The format of the selected line range can be: {3,}, {,10}, {1,10}

WARNING

Note that this does not throw errors if your file is not present. Hence, when using this feature make sure that the contents are being rendered as expected.

Math Equations

This is currently opt-in. To enable it, you need to install markdown-it-mathjax3 and set markdown.math to true in your config file:

sh
npm add -D markdown-it-mathjax3
npm add -D markdown-it-mathjax3
ts
// .vitepress/config.ts
export default {
  markdown: {
    math: true
  }
}
// .vitepress/config.ts
export default {
  markdown: {
    math: true
  }
}

Input

md
When $a \ne 0$, there are two solutions to $(ax^2 + bx + c = 0)$ and they are
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$

**Maxwell's equations:**

| equation                                                                                                                                                                  | description                                                                            |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| $\nabla \cdot \vec{\mathbf{B}}  = 0$                                                                                                                                      | divergence of $\vec{\mathbf{B}}$ is zero                                               |
| $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t}  = \vec{\mathbf{0}}$                                                          | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$ |
| $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}}    \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _wha?_                                                                                 |
When $a \ne 0$, there are two solutions to $(ax^2 + bx + c = 0)$ and they are
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$

**Maxwell's equations:**

| equation                                                                                                                                                                  | description                                                                            |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| $\nabla \cdot \vec{\mathbf{B}}  = 0$                                                                                                                                      | divergence of $\vec{\mathbf{B}}$ is zero                                               |
| $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t}  = \vec{\mathbf{0}}$                                                          | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$ |
| $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}}    \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _wha?_                                                                                 |

Output

When a0, there are two solutions to (ax2+bx+c=0) and they are

x=b±b24ac2a

Maxwell's equations:

equationdescription
B=0divergence of B is zero
×E+1cBt=0curl of E is proportional to the rate of change of B
×B1cEt=4πcjE=4πρwha?

Advanced Configuration

VitePress uses markdown-it as the Markdown renderer. A lot of the extensions above are implemented via custom plugins. You can further customize the markdown-it instance using the markdown option in .vitepress/config.js:

js
import markdownItAnchor from 'markdown-it-anchor'
import markdownItFoo from 'markdown-it-foo'

module.exports = {
  markdown: {
    // options for markdown-it-anchor
    // https://github.com/valeriangalliat/markdown-it-anchor#usage
    anchor: {
      permalink: markdownItAnchor.permalink.headerLink()
    },

    // options for @mdit-vue/plugin-toc
    // https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-toc#options
    toc: { level: [1, 2] },

    config: (md) => {
      // use more markdown-it plugins!
      md.use(markdownItFoo)
    }
  }
}
import markdownItAnchor from 'markdown-it-anchor'
import markdownItFoo from 'markdown-it-foo'

module.exports = {
  markdown: {
    // options for markdown-it-anchor
    // https://github.com/valeriangalliat/markdown-it-anchor#usage
    anchor: {
      permalink: markdownItAnchor.permalink.headerLink()
    },

    // options for @mdit-vue/plugin-toc
    // https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-toc#options
    toc: { level: [1, 2] },

    config: (md) => {
      // use more markdown-it plugins!
      md.use(markdownItFoo)
    }
  }
}

See full list of configurable properties in Config Reference: App Config.

Released under the MIT License.