Usage
Use the links prop with the page?.body?.toc?.links you get when fetching a page.
Highlighting the reader's position is handled for you. The
useScrollspy composable behind it is exported too, if you are building your own table of contents: const { activeHeadings, updateHeadings } = useScrollspy(). Call updateHeadings() with the heading elements to observe after the content changes. Prefer activeHeadings over visibleHeadings — it keeps the last heading lit while the reader is between two, where visibleHeadings goes empty and the highlight flickers.Title
Use the title prop to change the title of the Table of Contents.
<script setup lang="ts">
import type { ContentTocLink } from '@bitrix24/b24ui-nuxt'
const links = ref<ContentTocLink[]>([
{
id: 'usage',
depth: 2,
text: 'Usage',
children: [
{
id: 'title',
depth: 3,
text: 'Title'
}
]
},
{
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>
<B24ContentToc title="On this page" :links="links" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ContentTocLink } from '@bitrix24/b24ui-nuxt'
const links = ref<ContentTocLink[]>([
{
id: 'usage',
depth: 2,
text: 'Usage',
children: [
{
id: 'title',
depth: 3,
text: 'Title'
}
]
},
{
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>
<B24ContentToc title="On this page" :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>
<template v-if="page">
<ProseH1>{{ page.title }}</ProseH1>
<template v-if="page?.body?.toc?.links?.length">
<B24ContentToc :links="page.body.toc.links" />
</template>
<div>
<ContentRenderer v-if="page.body" :value="page" />
<B24Separator v-if="surround?.filter(Boolean).length" class="my-4" />
<B24ContentSurround :surround="(surround as any)" />
</div>
</template>
</template>
API
Props
Slots
Emits
Theme
https://github.com/bitrix24/b24ui/tree/main/src/theme/content/content-toc.ts
export default {
slots: {
root: 'flex flex-col lg:overflow-hidden',
container: 'border-dashed border-b-(length:--ui-border-width-thick) border-(--ui-color-divider-vibrant-default) lg:border-0 flex flex-col lg:min-h-0',
top: 'lg:shrink-0',
bottom: 'hidden lg:flex lg:flex-col lg:shrink-0 gap-[24px]',
trigger: 'group pb-[12px] cursor-pointer lg:cursor-text focus-visible:outline-(--ui-color-accent-main-primary) focus-visible:outline-2 focus-visible:rounded-[4px] text-legend text-(length:--ui-font-size-lg)/(--ui-font-line-height-3xs) font-(--ui-font-weight-semi-bold) flex-1 flex items-center gap-[6px] lg:shrink-0',
title: 'line-clamp-1 lg:line-clamp-2',
trailing: 'ms-auto inline-flex gap-[6px] items-center',
trailingIcon: 'size-[20px] transform transition-transform duration-200 motion-reduce:transition-none shrink-0 group-data-[state=open]:rotate-180 lg:hidden',
content: 'relative motion-safe:data-[state=open]:animate-[collapsible-down_200ms_var(--ease-out)] motion-safe:data-[state=closed]:animate-[collapsible-up_200ms_var(--ease-out)] overflow-hidden focus:outline-none lg:min-h-[min(var(--list-height,8rem),8rem)] lg:overflow-y-auto lg:scrollbar-thin lg:scrollbar-transparent',
list: 'min-w-0',
listWithChildren: 'ms-[12px]',
item: 'min-w-0',
itemWithChildren: '',
link: 'group relative text-(length:--ui-font-size-lg)/(--ui-font-line-height-3xs) flex items-center focus-visible:outline-(--ui-color-accent-main-primary) focus-visible:outline-1 focus-visible:rounded-[4px] text-description underline-offset-2 hover:text-label hover:underline pb-[12px]',
linkText: 'line-clamp-2'
},
variants: {
active: {
false: {
link: 'text-description hover:text-label transition-colors'
}
},
body: {
true: {
bottom: 'mt-6'
}
}
},
compoundVariants: [
{
active: true,
class: {
link: 'text-label',
linkLeadingIcon: 'text-label'
}
}
],
defaultVariants: {}
}