Skip to content

Toc

Generates a table of contents for the rendered Markdown.

Basic Usage

Use target to specify the element to generate the TOC. It supports CSS selector, DOM element, component instance, or a function that returns any of these. The default is document.documentElement.

View Source
vue
<template>
  <div style="display: flex; flex-wrap: wrap">
    <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
    <vmd-toc style="flex: auto" :target="renderRef" />
  </div>
</template>

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

const renderRef = ref(null)

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>

Plain Text

Use plain-text to generate a plain-text TOC. In this mode, clicking items will not trigger anchor scrolling.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="plainText" name="checkbox" type="checkbox" />
        Enable plain-text
      </label>
    </p>
    <div style="display: flex; flex-wrap: wrap">
      <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
      <vmd-toc style="flex: auto" :target="renderRef" :plain-text="plainText" />
    </div>
  </div>
</template>

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

const renderRef = ref(null)
const plainText = ref(true)

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>

Use Markdown Directly

Use markdown to generate a plain-text TOC directly from Markdown. If both target and markdown are provided, target takes precedence.

View Source
vue
<template>
  <vmd-toc :markdown="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>

Ordered List

Use ordered-list to generate an ordered TOC.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="orderedList" name="checkbox" type="checkbox" />
        Enable ordered-list
      </label>
    </p>
    <div style="display: flex; flex-wrap: wrap">
      <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
      <vmd-toc style="flex: auto" :target="renderRef" :ordered-list="orderedList" />
    </div>
  </div>
</template>

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

const renderRef = ref(null)
const orderedList = ref(true)

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>

Heading Level Range

Use start-level and end-level to control the heading level range used to build the 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>
    <div style="display: flex; flex-wrap: wrap">
      <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
      <vmd-toc
        style="flex: auto"
        :target="renderRef"
        :start-level="startLevel"
        :end-level="endLevel"
      />
    </div>
  </div>
</template>

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

const renderRef = ref(null)

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>

Ignore Headings

Use ignore to ignore specific heading levels when generating the TOC.

View Source
vue
<template>
  <div>
    <p>
      Select heading levels to ignore:
      <label v-for="(_, index) in ignoreList" :key="index" style="margin-right: 10px">
        <input v-model="ignoreList[index]" name="checkbox" type="checkbox" />
        h{{ index + 1 }}
      </label>
    </p>
    <div style="display: flex; flex-wrap: wrap">
      <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
      <vmd-toc style="flex: auto" :target="renderRef" :ignore="ignore" />
    </div>
  </div>
</template>

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

const renderRef = ref(null)

const ignore = ref([])
const ignoreList = ref([false, false, true, false, false, false])
watchEffect(() => {
  ignore.value = ignoreList.value.map((item, index) => Number(item) && index + 1).filter(Boolean)
})

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>

Empty Text

Use empty-text to customize the text displayed when the TOC is empty.

View Source
vue
<template>
  <div style="display: flex; flex-wrap: wrap">
    <vmd-render ref="renderRef" style="flex: 70%" :src="md" />
    <vmd-toc style="flex: auto" empty-text="Nothing to Show" :target="renderRef" />
  </div>
</template>
<script setup>
import { ref } from 'vue'

const renderRef = ref(null)

const md = `
  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>

Scroll Offset

When clicking a TOC item triggers anchor scrolling, you can use offset to adjust the scroll position. It supports numbers and string positions (center, start, end, nearest). When the component is nested inside multiple scroll containers, the scroll behavior and offset only apply to the nearest scroll container.

View Source
vue
<template>
  <div>
    <h4>Number:</h4>
    <div style="display: flex; flex-wrap: wrap">
      <div style="flex: 70%; overflow: auto; position: relative">
        <div
          style="
            width: 100%;
            background: #0969da;
            position: absolute;
            top: 0;
            color: #fff;
            padding: 16px 32px;
            box-sizing: border-box;
            z-index: 1;
          "
        >
          Fixed header
        </div>
        <div style="height: 436px; overflow: auto">
          <vmd-render ref="basicWhenNumber" style="margin-top: 56px" :src="md" />
        </div>
      </div>
      <vmd-toc style="flex: auto" :offset="56" :target="basicWhenNumber" />
    </div>
    <h4>String:</h4>
    <div style="display: flex; flex-wrap: wrap">
      <div style="flex: 70%; overflow: auto; position: relative">
        <div
          style="
            width: 100%;
            background: #0969da;
            position: absolute;
            top: 0;
            color: #fff;
            padding: 16px 32px;
            box-sizing: border-box;
            z-index: 1;
          "
        >
          Fixed header
        </div>
        <div style="height: 436px; overflow: auto">
          <vmd-render ref="basicWhenPosition" style="margin-top: 56px" :src="md" />
        </div>
      </div>
      <vmd-toc style="flex: auto" offset="end" :target="basicWhenPosition" />
    </div>
  </div>
</template>

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

const basicWhenNumber = ref(null)
const basicWhenPosition = ref(null)

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>

Smooth Scrolling

Use smooth to enable smooth scrolling during anchor navigation.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="smooth" name="checkbox" type="checkbox" />
        Enable smooth
      </label>
    </p>
    <div style="display: flex; flex-wrap: wrap">
      <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
      <vmd-toc style="flex: auto" :target="renderRef" :smooth="smooth" />
    </div>
  </div>
</template>

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

const renderRef = ref(null)
const smooth = ref(true)

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>

Update Page Hash

When clicking a TOC item triggers anchor navigation, you can use change-hash to control whether the page hash is updated.

View Source
vue
<template>
  <div>
    <p>
      <label>
        <input v-model="changeHash" name="checkbox" type="checkbox" />
        Enable change-hash
      </label>
    </p>
    <div style="display: flex; flex-wrap: wrap">
      <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
      <vmd-toc style="flex: auto" :target="renderRef" :change-hash="changeHash" />
    </div>
  </div>
</template>

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

const renderRef = ref(null)
const changeHash = ref(true)

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>

Manual Refresh

Use the refresh method to refresh the generated TOC manually.

TIP

If you use a Render component instance as the TOC target, manual refresh is usually unnecessary because Toc automatically refreshes when the content changes.

View Source
vue
<template>
  <div>
    <p>
      <label style="margin-right: 10px">
        Select content:
        <select v-model="index" name="select">
          <option v-for="(item, index) in options" :key="item.title" :value="index">
            {{ item.title }}
          </option>
        </select>
      </label>
      <button @click="refresh">Update</button>
    </p>
    <div style="display: flex; flex-wrap: wrap">
      <vmd-render id="refreshRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
      <vmd-toc ref="tocRef" target="#refreshRef" style="flex: auto" />
    </div>
  </div>
</template>

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

const options = [
  {
    title: 'Lorem',
    value:
      '# Lorem\nLorem 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.\n# Maecenas\nMaecenas at dolor sodales, fermentum enim vel, sagittis mi. Curabitur hendrerit erat nulla, ac dapibus erat aliquam egestas. Aliquam erat volutpat. Ut laoreet, turpis eget scelerisque consectetur, sapien purus aliquam ipsum, ut elementum augue odio sit amet tortor. Nunc id neque leo. Aliquam congue arcu nunc, at lacinia magna convallis at. Etiam ut consectetur elit, nec laoreet mi. Pellentesque at volutpat arcu. Vestibulum eget metus interdum, fringilla dolor quis, venenatis dui. Sed egestas, nunc convallis sollicitudin efficitur, orci dui sagittis enim, in rhoncus sem arcu ut justo. Donec sed nibh enim. Sed id massa eget nisi tincidunt semper ac ornare orci. Fusce felis mi, sollicitudin at nisi et, lacinia interdum quam. Ut elit est, condimentum nec iaculis a, dignissim feugiat purus.'
  },
  {
    title: 'Sed',
    value:
      '# Sed\nSed lectus nisl, blandit at volutpat et, lobortis ac urna. Etiam elementum id mauris a ultricies. Suspendisse rhoncus est justo, eu pellentesque turpis elementum quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vel dolor orci. Vestibulum rhoncus erat vitae molestie porttitor. Phasellus accumsan risus ut enim mollis, quis feugiat massa auctor. Donec vehicula convallis nisi sit amet dapibus. Integer felis erat, interdum eget erat non, facilisis vulputate augue. Ut sed tortor vitae ante venenatis rhoncus eget id tortor. Etiam molestie luctus ligula sit amet venenatis. Aliquam iaculis tristique sem, vitae convallis mi ultricies nec.\n# Nunc\nNunc vulputate neque sit amet nisi sollicitudin, vel luctus justo vehicula. In sed sapien vel tortor interdum ornare quis ac massa. Proin sapien leo, pretium nec tempus sed, fermentum in felis. Praesent viverra velit arcu, pretium ultricies ipsum viverra non. Cras malesuada cursus velit a tincidunt. Nullam pellentesque nunc mi, sed placerat mi pretium quis. Proin laoreet urna sed urna placerat convallis. Integer eget blandit augue. Etiam eget nisi nibh. Maecenas vel nunc non ipsum ultricies facilisis et id augue. Nullam rhoncus tristique rhoncus. Vivamus aliquet elit vitae dui consequat commodo. Aenean maximus velit sed pharetra fringilla. Aenean semper nunc eu massa lobortis, eu aliquam ante rhoncus.'
  }
]

const index = ref(0)
const md = ref(options[0].value)

const tocRef = ref(null)
const refresh = () => {
  md.value = options[index.value].value
  tocRef.value?.refresh()
}
</script>

Custom TOC Item

Use the item slot to customize TOC items.

View Source
vue
<template>
  <div style="display: flex; flex-wrap: wrap">
    <vmd-render ref="renderRef" style="height: 436px; overflow: auto; flex: 70%" :src="md" />
    <vmd-toc style="flex: auto" :target="renderRef">
      <template #item="tocItem">
        <div style="display: flex; justify-content: space-between">
          {{ tocItem.text }}
          <span v-if="tocItem.isActive">✅</span>
        </div>
      </template>
    </vmd-toc>
  </div>
</template>

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

const renderRef = ref(null)

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
targetThe target element used to build the TOCstring / HTMLElement / VueInstance / ()=> string | HTMLElement | VueInstancedocument.documentElement
plain-textWhether to generate a plain-text TOCbooleanfalse
markdownGenerate a plain-text TOC from Markdownstring''
ordered-listWhether to generate an ordered TOCbooleanfalse
start-levelStart heading level for the TOCnumber / string1
end-levelEnd heading level for the TOCnumber / string6
ignoreHeading levels to ignore in the TOCArray<number | string>[]
empty-textText displayed when the TOC is emptystringNo Data
offsetScroll offset during TOC anchor navigationnumber / 'start'|'end'|'center'|'nearest'start
smoothWhether to enable smooth scrolling when navigating via the TOCbooleanfalse
change-hashWhether to update the page hash when navigating via the TOCbooleantrue

Events

NameDescriptionArguments
clickTriggered when clicking a TOC item. For arguments, see TocItemtocItem: TocItem
changeTriggered when the active TOC item changesid: string

Expose

NameDescriptionType
scrollToScroll to a given heading in the TOC(href: string) => void
refreshRegenerate the TOC() => void

Slots

NameDescriptionSlotProps
itemCustomize TOC item content. For slot props, see TocItem.tocItem: TocItem

TocItem

KeyDescriptionType
idUnique ID for the TOC itemstring
levelHeading levelnumber
relativeLevelHeading level relative to start-level, always starting at 0number
textText content of the TOC itemstring
isActiveWhether the item is activeboolean
topScroll position of the corresponding headingnumber