ContentToc

GitHub
A sticky Table of Contents with automatic active anchor link highlighting.
This component is only available when the @nuxt/content module is installed.

Usage

Use the links prop with the page?.body?.toc?.links you get when fetching a page.

<script setup lang="ts">
import { createError, useAsyncData, useRoute } from '#app';
import { queryCollection } from '#imports';

const route = useRoute();

const { data: page } = await useAsyncData(route.path, () => queryCollection('docs').path(route.path).first());
if (!page.value) {
  throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true });
}
</script>

<template>
  <PContentToc :links="page?.body?.toc?.links" />
</template>

Title

Use the title prop to change the title of the Table of Contents.

<script setup lang="ts">
const links = ref([
  {
    id: 'usage',
    depth: 2,
    text: 'Usage',
    children: [
      {
        id: 'title',
        depth: 3,
        text: 'Title'
      },
      {
        id: 'color',
        depth: 3,
        text: 'Color'
      },
      {
        id: 'highlight',
        depth: 3,
        text: 'Highlight'
      }
    ]
  },
  {
    id: 'api',
    depth: 2,
    text: 'API',
    children: [
      {
        id: 'props',
        depth: 3,
        text: 'Props'
      },
      {
        id: 'slots',
        depth: 3,
        text: 'Slots'
      }
    ]
  },
  {
    id: 'theme',
    depth: 2,
    text: 'Theme'
  }
])
</script>

<template>
  <PContentToc title="On this page" :links="links" />
</template>

Color

Use the color prop to change the color of the links.

<script setup lang="ts">
const links = ref([
  {
    id: 'usage',
    depth: 2,
    text: 'Usage',
    children: [
      {
        id: 'title',
        depth: 3,
        text: 'Title'
      },
      {
        id: 'color',
        depth: 3,
        text: 'Color'
      },
      {
        id: 'highlight',
        depth: 3,
        text: 'Highlight'
      }
    ]
  }
])
</script>

<template>
  <PContentToc color="neutral" :links="links" />
</template>

Highlight

Use the highlight prop to display a highlighted border for the active item.

Use the highlight-color prop to change the color of the border. It defaults to the color prop.

<script setup lang="ts">
const links = ref([
  {
    id: 'usage',
    depth: 2,
    text: 'Usage',
    children: [
      {
        id: 'title',
        depth: 3,
        text: 'Title'
      },
      {
        id: 'color',
        depth: 3,
        text: 'Color'
      },
      {
        id: 'highlight',
        depth: 3,
        text: 'Highlight'
      }
    ]
  }
])
</script>

<template>
  <PContentToc highlight highlight-color="neutral" color="neutral" :links="links" />
</template>

Examples

Within a page

Use the ContentToc component in a page to display the Table of Contents:

pages/[...slug].vue
<script setup lang="ts">
const route = useRoute();

const { data: page } = await useAsyncData(route.path, () => queryCollection('docs').path(route.path).first());
if (!page.value) {
  throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true });
}
</script>

<template>
  <ExamplePage v-if="page">
    <ExamplePageHeader :title="page.title" />

    <ExamplePageBody>
      <ContentRenderer
        v-if="page.body"
        :value="page"
      />

      <PSeparator v-if="surround?.filter(Boolean).length" />

      <PContentSurround :surround="(surround as any)" />
    </ExamplePageBody>

    <template
      v-if="page?.body?.toc?.links?.length"
      #right
    >
      <PContentToc :links="page.body.toc.links" />
    </template>
  </ExamplePage>
</template>

API

Props

Prop Default Type

Slots

Slot Type

Emits

Event Type

Theme

We use unocss-variants to customize the theme. Read more about it in the theming guide.

Below is the theme configuration skeleton for the PContentToc. Since the component is provided unstyled by default, you will need to fill in these values to apply your own custom look and feel. If you prefer to use our pre-built, opinionated styling, you can instead use our UnoCSS preset, this docs is using it as well.

app.config.ts
export default defineAppConfig({
  pohon: {
    contentToc: {
      slots: {
        root: '',
        container: '',
        top: '',
        bottom: '',
        trigger: '',
        title: '',
        trailing: '',
        trailingIcon: '',
        content: '',
        list: '',
        listWithChildren: '',
        item: '',
        itemWithChildren: '',
        link: '',
        linkText: '',
        indicator: ''
      },
      variants: {
        color: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        highlightColor: {
          primary: {
            indicator: ''
          },
          secondary: {
            indicator: ''
          },
          success: {
            indicator: ''
          },
          info: {
            indicator: ''
          },
          warning: {
            indicator: ''
          },
          error: {
            indicator: ''
          },
          neutral: {
            indicator: ''
          }
        },
        active: {
          false: {
            link: ''
          }
        },
        highlight: {
          true: {
            list: '',
            item: ''
          }
        },
        body: {
          true: {
            bottom: ''
          }
        }
      },
      defaultVariants: {
        color: 'primary',
        highlightColor: 'primary'
      }
    }
  }
};
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import pohon from 'pohon-ui/vite'

export default defineAppConfig({
  pohon: {
    contentToc: {
      slots: {
        root: '',
        container: '',
        top: '',
        bottom: '',
        trigger: '',
        title: '',
        trailing: '',
        trailingIcon: '',
        content: '',
        list: '',
        listWithChildren: '',
        item: '',
        itemWithChildren: '',
        link: '',
        linkText: '',
        indicator: ''
      },
      variants: {
        color: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        highlightColor: {
          primary: {
            indicator: ''
          },
          secondary: {
            indicator: ''
          },
          success: {
            indicator: ''
          },
          info: {
            indicator: ''
          },
          warning: {
            indicator: ''
          },
          error: {
            indicator: ''
          },
          neutral: {
            indicator: ''
          }
        },
        active: {
          false: {
            link: ''
          }
        },
        highlight: {
          true: {
            list: '',
            item: ''
          }
        },
        body: {
          true: {
            bottom: ''
          }
        }
      },
      defaultVariants: {
        color: 'primary',
        highlightColor: 'primary'
      }
    }
  }
};

Changelog

No recent changes