Skip to content

Markdown

Provides real-time Markdown rendering, table of contents generation, and full-text search.

Basic Usage

Use src to pass a Markdown string and render the corresponding HTML output.

View Source
vue
<template>
  <vue-markdown :src="md" />
</template>

<script setup>
const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

Parse HTML Tags

Use html to control whether HTML tags inside Markdown are parsed.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="html" name="checkbox" type="checkbox" />
        Enable html
      </label>
    </p>
    <vue-markdown :src="md" :html="html" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const html = ref(true)

const md = `
  # Lorem ipsum

  <p style="background: rgba(9, 105, 218, 0.14); padding: 10px">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi maximus elit fermentum pellentesque vehicula. Suspendisse potenti. Donec iaculis consectetur erat nec placerat. Suspendisse facilisis justo sit amet hendrerit sollicitudin. Suspendisse commodo malesuada massa, ac elementum risus. Ut eu facilisis neque. Fusce tincidunt, ligula vitae eleifend venenatis, purus purus ultrices purus, nec maximus tellus lectus nec leo. Sed auctor magna sed quam dapibus dapibus. Nullam ornare ultricies sem, a iaculis sapien volutpat euismod. Sed ac dictum nulla. Duis euismod tellus vitae diam hendrerit, sit amet vestibulum mauris rhoncus.
  </p>
  `
</script>

Sanitize HTML Tags

Use sanitize to sanitize HTML tags in the output to prevent XSS and other web attacks. Enabling sanitize may affect how some HTML tags are rendered. If the result is not what you expect and your security requirements are low, you can try disabling sanitize, but this is extremely risky. If you have strict security requirements, consider disabling html instead.

View Source
vue
<template>
  <div>
    <h4>Markdown:</h4>
    <pre style="background: #fff; overflow: auto"><code>{{ md }}</code></pre>
    <h4>Output:</h4>
    <vue-markdown :src="md" />
  </div>
</template>

<script setup>
const md = `
  <p style="background: rgba(9, 105, 218, 0.14); padding: 10px">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    <iframe//src=jAva&Tab;script:alert(3)>def
  </p>
  `
</script>

Preset Name

Use preset-name to quickly switch Markdown syntax rules. Three commonly used presets are built in: default (similar to GFM), commonmark (see CommonMark), and zero (disables all syntax rules).

TIP

Compared to markdown-it's default preset, the html option is enabled by default here.

View Source
vue
<template>
  <div>
    <p>
      <label>
        Select a preset:
        <select v-model="presetName" name="select">
          <option v-for="item in options" :key="item" :value="item">{{ item }}</option>
        </select>
      </label>
    </p>
    <template v-for="item in options" :key="item">
      <vue-markdown v-if="item === presetName" :src="md" :preset-name="item" />
    </template>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const options = ['default', 'commonmark', 'zero']
const presetName = ref('default')

const md = `
  | Preset Name                                                                                     | Description                               |
  | ----------------------------------------------------------------------------------------------- | ----------------------------------------- |
  | [default](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/default.mjs)       | Similar to GFM                            |
  | [commonmark](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/commonmark.mjs) | See [CommonMark](https://commonmark.org/) |
  | [zero](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/zero.mjs)             | Disables all syntax rules                 |
  `
</script>

markdown-it Plugins

Use plugins to pass markdown-it plugins. Two forms are supported: pass a single plugin directly, or pass an array of plugins where each item can be a plugin or a plugin-with-params tuple (e.g., [plugin, [plugin, param1, param2, ...], ...]).

View Source
vue
<template>
  <vue-markdown :src="md" :plugins="plugins" />
</template>

<script setup>
// npm i markdown-it-container
import container from 'markdown-it-container'

const md = `
  # Lorem ipsum

  ::: example
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi maximus elit fermentum pellentesque vehicula. Suspendisse potenti. Donec iaculis consectetur erat nec placerat. Suspendisse facilisis justo sit amet hendrerit sollicitudin. Suspendisse commodo malesuada massa, ac elementum risus. Ut eu facilisis neque. Fusce tincidunt, ligula vitae eleifend venenatis, purus purus ultrices purus, nec maximus tellus lectus nec leo. Sed auctor magna sed quam dapibus dapibus. Nullam ornare ultricies sem, a iaculis sapien volutpat euismod. Sed ac dictum nulla. Duis euismod tellus vitae diam hendrerit, sit amet vestibulum mauris rhoncus.
  :::
  `

const plugins = [
  [
    container,
    'example',
    {
      render: (tokens, idx) => {
        return tokens[idx].nesting === 1 ? '<details><summary>Expand</summary>\n' : '</details>\n'
      }
    }
  ]
]
</script>

Syntax Highlighting

Use highlight to syntax-highlight fenced code blocks based on their language. When a boolean is provided, highlight.js is used as the syntax highlighter and can be configured via highlight-options (see highlight.js configuration). If the default highlighting is not enough, highlight also supports providing a function to customize highlighting.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="highlight" name="checkbox" type="checkbox" />
        Enable highlight
      </label>
    </p>
    <h4>Boolean:</h4>
    <vue-markdown :src="md" :highlight="highlight" />
    <h4>Function:</h4>
    <vue-markdown :src="md" :highlight="highlight && customHighlight" />
  </div>
</template>

<script setup>
// npm i highlight.js
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
import { ref } from 'vue'

const highlight = ref(true)
const customHighlight = (str, language) => {
  return hljs.highlight(str, { language }).value
}

const md = `
  \`\`\` js
  var foo = function (bar) {
    return bar++;
  };

  console.log(foo(5));
  \`\`\`
  `
</script>

Custom Highlight Class

Use lang-prefix to change the CSS class prefix used for code blocks, so you can customize highlight styles.

View Source
vue
<template>
  <div>
    <p>
      <label>
        Select class prefix:
        <select v-model="langPrefix" name="select">
          <option value="custom-">Custom prefix: custom-</option>
          <option value="language-">Default prefix: language-</option>
        </select>
      </label>
    </p>
    <vue-markdown :src="md" :lang-prefix="langPrefix" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const langPrefix = ref('custom-')

const md = `
  \`\`\` js
  var foo = function (bar) {
    return bar++;
  };

  console.log(foo(5));
  \`\`\`
  `
</script>

<style>
.custom-js {
  color: #666666 !important;
}
.custom-js .hljs-keyword {
  color: #ab5959 !important;
}
.custom-js .hljs-title {
  color: #59873a !important;
}
.custom-js .hljs-variable,
.custom-js .hljs-params {
  color: #b07d48 !important;
}
.custom-js .hljs-string {
  color: #b56959 !important;
}
</style>

Linkify

Use linkify to automatically convert URL-like text in Markdown into links.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="linkify" name="checkbox" type="checkbox" />
        Enable linkify
      </label>
    </p>
    <vue-markdown :src="md" :linkify="linkify" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const linkify = ref(true)

const md = `
  Link: [https://www.example.com](https://www.example.com)

  URL-like text: https://www.example.com
  `
</script>

Typographer

Use typographer to replace certain text in Markdown with typographic symbols. When typographer is enabled, you can also customize quotation marks via quotes, which is useful in multilingual environments.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="typographer" name="checkbox" type="checkbox" />
        Enable typographer
      </label>
    </p>
    <p>
      <label>
        Enter quotes:
        <input
          v-for="(item, index) in quotes"
          :key="item"
          v-model="quotes[index]"
          name="text"
          style="width: 2em; margin: 4px"
        />
      </label>
    </p>
    <vue-markdown :src="md" :typographer="typographer" :quotes="[...quotes]" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const typographer = ref(true)
const quotes = ref(['«', '»', '‹', '›'])

const md = `
  Copyright symbol: (c)

  Trademark: (tm)

  Registered trademark: (r)

  "This is a double-quote demo"

  'This is a single-quote demo'
  `
</script>

Emoji

Use emoji to parse specific text in Markdown into emoji. In addition to boolean values, you can also pass an options object (see markdown-it-emoji options).

View Source
vue
<template>
  <vue-markdown :src="md" :emoji="emoji" />
</template>

<script setup>
const emoji = {
  shortcuts: {
    smiley: ':-)',
    frowning: ':-(',
    sunglasses: '8-)',
    wink: ';)'
  }
}

const md = `
  Classic emojis: :wink: :cry: :laughing: :yum:

  Custom shortcuts: :-) :-( 8-) ;)
  `
</script>

Heading Anchors

Use anchor to add unique IDs to all headings. This helps navigation components (such as Toc) perform anchor scrolling. When anchor is enabled, permalinks are enabled by default for all headings; you can toggle them with permalink. anchor also supports passing an options object or a function to customize heading rendering (see markdown-it-anchor options).

View Source
vue
<template>
  <div>
    <h4>Default heading rendering:</h4>
    <p>
      <label>
        <input v-model="permalink" name="checkbox" type="checkbox" />
        Enable permalink (hover the heading to show the permalink symbol)
      </label>
    </p>
    <vue-markdown :src="md" :permalink="permalink" />
    <h4>Custom heading rendering:</h4>
    <vue-markdown :src="md" :anchor="anchorOptions" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const permalink = ref(true)

const anchorOptions = (anchor) => ({
  permalink: anchor.permalink.headerLink()
})

const md = `
  # Lorem ipsum

  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi maximus elit fermentum pellentesque vehicula. Suspendisse potenti. Donec iaculis consectetur erat nec placerat. Suspendisse facilisis justo sit amet hendrerit sollicitudin. Suspendisse commodo malesuada massa, ac elementum risus. Ut eu facilisis neque. Fusce tincidunt, ligula vitae eleifend venenatis, purus purus ultrices purus, nec maximus tellus lectus nec leo. Sed auctor magna sed quam dapibus dapibus. Nullam ornare ultricies sem, a iaculis sapien volutpat euismod. Sed ac dictum nulla. Duis euismod tellus vitae diam hendrerit, sit amet vestibulum mauris rhoncus.
  `
</script>

More Render Options

The rendering features support rich configuration such as custom Markdown styles, accessing the internal markdown-it instance, and breaks. For more details, see the Render. Its API also applies to the Markdown component.

View Source
vue
<template>
  <div>
    <p>
      <label>
        Select a class name:
        <select v-model="markdownClass" name="select">
          <option value="custom-theme">Custom: custom-theme</option>
          <option value="markdown-body">Default: markdown-body</option>
          <option value="">No theme</option>
        </select>
      </label>
    </p>
    <vue-markdown :src="md" :markdown-class="markdownClass" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const markdownClass = ref('custom-theme')

const md = `
  # Lorem ipsum

  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi maximus elit fermentum pellentesque vehicula. Suspendisse potenti. Donec iaculis consectetur erat nec placerat. Suspendisse facilisis justo sit amet hendrerit sollicitudin. Suspendisse commodo malesuada massa, ac elementum risus. Ut eu facilisis neque. Fusce tincidunt, ligula vitae eleifend venenatis, purus purus ultrices purus, nec maximus tellus lectus nec leo. Sed auctor magna sed quam dapibus dapibus. Nullam ornare ultricies sem, a iaculis sapien volutpat euismod. Sed ac dictum nulla. Duis euismod tellus vitae diam hendrerit, sit amet vestibulum mauris rhoncus.
  `
</script>

<style>
.custom-theme {
  background: #fffffd;
}

.custom-theme * {
  margin: 0;
}

.custom-theme h1 {
  font-size: 2.4em;
  padding-bottom: 0.3em;
  border-bottom: 3px double #eee;
  margin-bottom: 0.6em;
  line-height: 1.35;
  font-weight: bolder;
  color: #456688;
}

.custom-theme p {
  margin-bottom: 1.2em;
  color: #383838;
}

.custom-theme a {
  color: #456688;
  text-decoration: none;
}
</style>

Use search to enable searching. You can also bind the keyword via keyword. When the keyword is present, it will automatically scroll to the match and highlight it.

View Source
vue
<template>
  <vue-markdown
    v-model:keyword="keyword"
    v-model:search="search"
    style="height: 436px; overflow: auto"
    :src="md"
  />
</template>

<script setup>
import { ref } from 'vue'

const search = ref(true)
const keyword = ref('or')

const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

More Search Options

The search feature supports rich configuration such as border, size, and disabled. For more details, see the Search. When using it through the Markdown component, prefix Search props in its API with search-.

View Source
vue
<template>
  <div>
    <p>
      <label style="margin-right: 10px">
        Enter index:
        <input v-model="index" name="number" type="number" style="width: 4em" />
      </label>
      <button @click="markdownRef.searchToggle(index)">Go</button>
    </p>
    <p>Current index (starting at 0): {{ safeIndex }}</p>
    <vue-markdown
      v-model:keyword="keyword"
      v-model:search="search"
      ref="markdownRef"
      style="height: 436px; overflow: auto"
      :src="md"
      @search-index-change="safeIndex = $event"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const markdownRef = ref(null)

const search = ref(true)
const keyword = ref('or')

const index = ref(0)
const safeIndex = ref(0)

const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

TOC

Use toc to enable the TOC feature. The component generates a TOC based on the rendered content.

View Source
vue
<template>
  <vue-markdown toc top-offset="64" :src="md" />
</template>

<script setup>
const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

More TOC Options

The TOC feature supports rich configuration such as plain text, ordered list, and heading level range. For more details, see the Toc. When using it through the Markdown component, prefix Toc props in its API with toc-.

View Source
vue
<template>
  <div>
    <p>
      <label>
        Select start-level:
        <select v-model="startLevel" name="select">
          <option v-for="key in options" :key="key" :value="key">h{{ key }}</option>
        </select>
      </label>
    </p>
    <p>
      <label>
        Select end-level:
        <select v-model="endLevel" name="select">
          <option v-for="key in options" :key="key" :value="key">h{{ key }}</option>
        </select>
      </label>
    </p>
    <vue-markdown
      toc
      style="height: 436px; overflow: auto"
      :src="md"
      :toc-start-level="startLevel"
      :toc-end-level="endLevel"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const startLevel = ref(2)
const endLevel = ref(5)

const options = Array.from({ length: 6 }, (_, index) => index + 1)

const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

Action Buttons

Use show-btn to display a group of action buttons at the bottom of the component, used to toggle the visibility of Search and TOC. When set to true, both search and TOC buttons are shown by default. When set to an array or object, you can customize which buttons are displayed.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="showBtn.search" name="checkbox" type="checkbox" />
        Show search button
      </label>
      <label>
        <input v-model="showBtn.toc" name="checkbox" type="checkbox" />
        Show TOC button
      </label>
    </p>
    <vue-markdown :src="md" :show-btn="showBtn" />
  </div>
</template>
<script setup>
import { ref } from 'vue'

const showBtn = ref({ toc: true, search: true })

const md = `
  # Lorem ipsum

  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi maximus elit fermentum pellentesque vehicula. Suspendisse potenti. Donec iaculis consectetur erat nec placerat. Suspendisse facilisis justo sit amet hendrerit sollicitudin. Suspendisse commodo malesuada massa, ac elementum risus. Ut eu facilisis neque. Fusce tincidunt, ligula vitae eleifend venenatis, purus purus ultrices purus, nec maximus tellus lectus nec leo. Sed auctor magna sed quam dapibus dapibus. Nullam ornare ultricies sem, a iaculis sapien volutpat euismod. Sed ac dictum nulla. Duis euismod tellus vitae diam hendrerit, sit amet vestibulum mauris rhoncus.
  `
</script>

Sticky Offset

All helper components use sticky positioning. Use top-offset to control the sticky top offset for the search and TOC, and bottom-offset to control the sticky bottom offset for the action buttons. When nested inside multiple scroll containers, sticky behavior and offsets only apply to the nearest scroll container.

View Source
vue
<template>
  <div style="position: relative">
    <header
      style="
        width: 100%;
        background: #0969da;
        position: absolute;
        top: 0;
        color: #fff;
        padding: 16px 32px;
        box-sizing: border-box;
        z-index: 5;
      "
    >
      Fixed header
    </header>
    <div style="height: 492px; overflow: auto">
      <vue-markdown
        toc
        search
        show-btn
        style="margin: 56px 0"
        top-offset="56"
        bottom-offset="56"
        :src="md"
      />
    </div>
    <footer
      style="
        width: 100%;
        background: #0969da;
        position: absolute;
        bottom: 0;
        color: #fff;
        padding: 16px 32px;
        box-sizing: border-box;
        z-index: 5;
      "
    >
      Fixed footer
    </footer>
  </div>
</template>

<script setup>
const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

Scroll Offset

By default, top-offset is also used as the scroll offset for TOC anchor navigation. To set a TOC offset separately, use toc-offset. Search index navigation does not inherit this behavior; use search-offset to set it explicitly. Both toc-offset and search-offset support numbers and string positions (center, start, end, nearest). When nested inside multiple scroll containers, scrolling behavior and offsets only apply to the nearest scroll container.

TIP

When the search is visible and toc-offset is a number or start, the TOC will automatically add the search input height as an extra scroll offset.

View Source
vue
<template>
  <div>
    <h4>Default:</h4>
    <div style="flex: auto; position: relative; margin-bottom: 16px">
      <header
        style="
          width: 100%;
          background: #0969da;
          position: absolute;
          top: 0;
          color: #fff;
          padding: 16px 32px;
          box-sizing: border-box;
          z-index: 5;
        "
      >
        Fixed header
      </header>
      <div style="height: 436px; overflow: auto">
        <vue-markdown
          v-model:keyword="keyword"
          toc
          search
          style="margin-top: 56px"
          top-offset="56"
          :src="md"
        />
      </div>
    </div>
    <h4>Number:</h4>
    <div style="flex: auto; position: relative; margin-bottom: 16px">
      <header
        style="
          width: 100%;
          background: #0969da;
          position: absolute;
          top: 0;
          color: #fff;
          padding: 16px 32px;
          box-sizing: border-box;
          z-index: 5;
        "
      >
        Fixed header
      </header>
      <div
        style="
          position: absolute;
          top: 160px;
          color: #0969da;
          padding: 10px;
          box-sizing: border-box;
          z-index: 1;
          border-bottom: 2px solid #0969da;
          width: 100%;
          font-weight: bold;
          text-align: center;
        "
      >
        Scroll offset marker
      </div>
      <div style="height: 436px; overflow: auto">
        <!-- 57 is the height of the search input itself -->
        <vue-markdown
          v-model:keyword="keyword"
          toc
          search
          style="margin-top: 56px"
          top-offset="56"
          toc-offset="150"
          :search-offset="150 + 57"
          :src="md"
        />
      </div>
    </div>
    <h4>String:</h4>
    <div style="flex: auto; position: relative; margin-bottom: 16px">
      <header
        style="
          width: 100%;
          background: #0969da;
          position: absolute;
          top: 0;
          color: #fff;
          padding: 16px 32px;
          box-sizing: border-box;
          z-index: 5;
        "
      >
        Fixed header
      </header>
      <div style="height: 436px; overflow: auto">
        <vue-markdown
          v-model:keyword="keyword"
          toc
          search
          style="margin-top: 56px"
          top-offset="56"
          toc-offset="end"
          search-offset="end"
          :src="md"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const keyword = ref('or')

const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

Mobile Layout

Use mini-screen-width to set the width threshold for mobile devices. When the component width is less than or equal to this value, it will automatically switch to the mobile layout.

View Source
vue
<template>
  <div style="overflow: auto">
    <p>
      <label>
        Adjust mini-screen-width:
        <input v-model="miniScreenWidth" style="width: 4em" name="number" type="number" min="230" />
      </label>
    </p>
    <p>
      <label>
        Adjust component width:
        <input v-model="componentWidth" style="width: 4em" name="number" type="number" min="230" />
      </label>
    </p>
    <vue-markdown
      toc
      show-btn
      :style="{ height: '436px', overflow: 'auto', width: `${componentWidth}px` }"
      :src="md"
      :mini-screen-width="miniScreenWidth"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const miniScreenWidth = ref(768)
const componentWidth = ref(768)

const md = `
  # h1 Heading 8-)
  ## h2 Heading
  ### h3 Heading
  #### h4 Heading
  ##### h5 Heading
  ###### h6 Heading


  ## Horizontal Rules

  ___

  ---

  ***


  ## Emphasis

  **This is bold text**

  __This is bold text__

  *This is italic text*

  _This is italic text_

  ~~Strikethrough~~


  ## Blockquotes

  > Blockquotes can also be nested...
  >> ...by using additional greater-than signs right next to each other...
  > > > ...or with spaces between arrows.


  ## Lists

  Unordered

  + Create a list by starting a line with \`+\`, \`-\`, or \`*\`
  + Sub-lists are made by indenting 2 spaces:
    - Marker character change forces new list start:
      * Ac tristique libero volutpat at
      + Facilisis in pretium nisl aliquet
      - Nulla volutpat aliquam velit
  + Very easy!

  Ordered

  1. Lorem ipsum dolor sit amet
  2. Consectetur adipiscing elit
  3. Integer molestie lorem at massa


  1. You can use sequential numbers...
  1. ...or keep all the numbers as \`1.\`

  Start numbering with offset:

  57. foo
  1. bar


  ## Tables

  | Option | Description |
  | ------ | ----------- |
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |

  Right aligned columns

  | Option | Description |
  | ------:| -----------:|
  | data   | path to data files to supply the data that will be passed into templates. |
  | engine | engine to be used for processing templates. Handlebars is the default. |
  | ext    | extension to be used for dest files. |
  `
</script>

API

Props

NameDescriptionTypeDefault
srcMarkdown stringstring / number''
htmlWhether to parse HTML tags in Markdownbooleantrue
sanitizeWhether to sanitize HTML tags in the output. Accepts a boolean or DOMPurify optionsboolean / SanitizeOptionstrue
preset-nameMarkdown preset name'default' | 'commonmark' | 'zero'default
pluginsmarkdown-it pluginsPluginSimple / Array<PluginSimple | [PluginWithParams, ...any[]]>[]
inlineWhether to enable inline modebooleanfalse
xhtml-outWhether to generate XHTML-compliant outputbooleanfalse
breaksWhether to render \n as <br>booleanfalse
highlightWhether to enable syntax highlighting for code blocksboolean / (str: string, lang: string, attrs: string) => stringtrue
highlight-optionsSyntax highlighting options. Only effective when highlight is true. See highlight.js configurationHLJSOptions-
lang-prefixCSS class prefix for code blocksstringlanguage-
linkifyWhether to convert URL-like text into linksbooleanfalse
typographerWhether to replace certain text with common typographic symbolsbooleanfalse
quotesCustom quotation marks. Only effective when typographer is enabledstring / Array<string>“”‘’
emojiWhether to parse specific text into emojis. Accepts a boolean or markdown-it-emoji optionsboolean / EmojiOptionstrue
anchorHeading anchor configuration. Accepts a boolean, markdown-it-anchor options, or a functionboolean / AnchorOptions / (anchor: Anchor) => AnchorOptionstrue
permalinkWhether to enable permalinks. Only effective when anchor is truebooleantrue
markdown-classCustom Markdown CSS classstringmarkdown-body
show-btnWhether to show the bottom action buttonsboolean / Array<('search' | 'toc')> / { search?: boolean; toc?: boolean }false
top-offsetGlobal top offset: sticky top offset and the default scroll offset for TOC anchor navigationnumber / string0
bottom-offsetGlobal bottom offset for sticky bottom positioningnumber / string0
mini-screen-widthMobile width threshold. When the component width is less than or equal to this value, it switches to the mobile layoutnumber / string768
v-model:searchWhether to show the searchbooleanfalse
v-model:keywordSearch keywordstring / number''
search-clearableWhether to show the clear button in the searchbooleantrue
search-close-iconWhether to show the close button in the searchbooleantrue
search-borderWhether to show the bottom border of the searchbooleantrue
search-sizeSearch input size'huge' | 'large' | 'normal' | 'small'huge
search-disabledWhether to disable the searchbooleanfalse
search-placeholderSearch input placeholder textstring-
search-offsetScroll offset when switching the search match indexnumber / 'start'|'end'|'center'|'nearest'center
search-smoothWhether to enable smooth scrolling when navigating between search matchesbooleanfalse
search-input-attrsAttributes passed to the internal search input elementobject{}
v-model:tocWhether to show the TOCbooleanfalse
toc-plain-textWhether to generate a plain-text TOCbooleanfalse
toc-ordered-listWhether to generate an ordered TOCbooleanfalse
toc-start-levelStart heading level for the TOCnumber / string1
toc-end-levelEnd heading level for the TOCnumber / string6
toc-ignoreHeading levels to ignore in the TOCArray<number | string>[]
toc-empty-textText displayed when the TOC is emptystringNo Data
toc-offsetScroll offset during TOC anchor navigationnumber / 'start'|'end'|'center'|'nearest'start
toc-smoothWhether to enable smooth scrolling when navigating via the TOCbooleanfalse
toc-change-hashWhether to update the page hash when navigating via the TOCbooleantrue

Events

NameDescriptionArguments
env-changeTriggered when env in markdown-it changesvalue: {}
search-inputTriggered when the search keyword changesevent: Event
search-blurTriggered when the search input loses focusevent: FocusEvent
search-focusTriggered when the search input gains focusevent: FocusEvent
search-changeTriggered only when the search input blurs or Enter is pressedevent: Event
search-clearTriggered when clicking the search clear button-
search-closeTriggered when clicking the search close button-
search-step-clickTriggered when clicking the search previous/next buttonsvalue: 'prev' | 'next'
search-total-changeTriggered when the total search match count changesvalue: number
search-index-changeTriggered when the current search match index changesvalue: number
toc-clickTriggered when clicking a TOC item. For arguments see TocItemtocItem: TocItem
toc-changeTriggered when the active TOC item changesid: string

Expose

NameDescriptionType
mdInstanceGet the internal markdown-it instanceMarkdownIt
htmlStrGet the parsed HTML stringstring
searchFocusFocus the search input() => void
searchBlurBlur the search input() => void
searchClearClear the search keyword() => void
searchToggleSwitch the current search match index(index: number | 'prev' | 'next', ignoreDisabled?: boolean) => void
searchRefreshRefresh search results manually(resetIndex?: boolean) => void
tocScrollToScroll to a given heading in the TOC(href: string) => void
tocRefreshRegenerate the TOC() => void