<script setup>
import { TableOfContents } from "@dicehub/phi/components/table-of-contents";
const headings = [
{ text: "Introduction" },
{ text: "Installation" },
{ text: "Usage" },
{ text: "API Reference" },
{ text: "Examples" },
];
</script>
<template>
<div class="min-w-48">
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
<TableOfContents.Item
v-for="heading in headings"
:key="heading.text"
:active="heading.text === 'Usage'"
class-name="cursor-pointer"
>
{{ heading.text }}
</TableOfContents.Item>
</TableOfContents.List>
</TableOfContents>
</div>
</template>Installation
Barrel
import { TableOfContents } from "@dicehub/phi";Granular
import {
TableOfContents,
useTableOfContentsActiveId,
} from "@dicehub/phi/components/table-of-contents";Usage
<script setup>
import { TableOfContents } from "@dicehub/phi/components/table-of-contents";
</script>
<template>
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
<TableOfContents.Item href="#intro" active>
Introduction
</TableOfContents.Item>
<TableOfContents.Item href="#api">API Reference</TableOfContents.Item>
</TableOfContents.List>
</TableOfContents>
</template>The component remains presentational: control each item's active prop directly, or pair it with useTableOfContentsActiveId for scroll tracking, hash selection, and custom scroll containers.
Examples
Interactive
Click an item to set it as active. The consumer controls state via active and @click.
<script setup>
import { ref } from "vue";
import { TableOfContents } from "@dicehub/phi/components/table-of-contents";
const headings = [
{ text: "Introduction" },
{ text: "Installation" },
{ text: "Usage" },
{ text: "API Reference" },
{ text: "Examples" },
];
const active = ref("Introduction");
</script>
<template>
<div class="min-w-48">
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
<TableOfContents.Item
v-for="heading in headings"
:key="heading.text"
:active="heading.text === active"
class-name="cursor-pointer"
@click="active = heading.text"
>
{{ heading.text }}
</TableOfContents.Item>
</TableOfContents.List>
</TableOfContents>
</div>
</template>No active item
When no item has active set, all items show the default subtle text style with a hover indicator.
<script setup>
import { TableOfContents } from "@dicehub/phi/components/table-of-contents";
const headings = [
{ text: "Introduction" },
{ text: "Installation" },
{ text: "Usage" },
{ text: "API Reference" },
{ text: "Examples" },
];
</script>
<template>
<div class="min-w-48">
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
<TableOfContents.Item
v-for="heading in headings"
:key="heading.text"
class-name="cursor-pointer"
>
{{ heading.text }}
</TableOfContents.Item>
</TableOfContents.List>
</TableOfContents>
</div>
</template>Scroll tracking
Pair the component with useTableOfContentsActiveId to track the viewport or a custom scroll root, handle hash navigation, and pin clicked sections until scrolling settles.
<script setup>
import { ref } from "vue";
import {
TableOfContents,
useTableOfContentsActiveId,
} from "@dicehub/phi/components/table-of-contents";
const root = ref(null);
const sections = [
{ id: "overview", title: "Overview" },
{ id: "installation", title: "Installation" },
{ id: "usage", title: "Usage" },
{ id: "api", title: "API" },
];
const { activeId, selectSection } = useTableOfContentsActiveId({
ids: sections.map((section) => section.id),
root,
trackHash: false,
});
function goToSection(id) {
selectSection(id);
root.value?.querySelector(`#${id}`)?.scrollIntoView({ behavior: "smooth" });
}
</script>
<template>
<TableOfContents>
<TableOfContents.List>
<TableOfContents.Item
v-for="section in sections"
:key="section.id"
as="button"
:active="activeId === section.id"
@click="goToSection(section.id)"
>
{{ section.title }}
</TableOfContents.Item>
</TableOfContents.List>
</TableOfContents>
<div ref="root" class="h-64 overflow-y-auto">
<section v-for="section in sections" :id="section.id" :key="section.id">
{{ section.title }}
</section>
</div>
</template>Groups
Use TableOfContents.Group to organize items into labeled sections with indented children. Pass href to make the group label a clickable link, or omit it for a plain non-interactive title.
<script setup>
import { TableOfContents } from "@dicehub/phi/components/table-of-contents";
</script>
<template>
<div class="min-w-48">
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
<TableOfContents.Item active class-name="cursor-pointer">
Overview
</TableOfContents.Item>
<TableOfContents.Group label="Examples" href="#examples-demo">
<TableOfContents.Item class-name="cursor-pointer">Basic example</TableOfContents.Item>
<TableOfContents.Item class-name="cursor-pointer">Advanced example</TableOfContents.Item>
</TableOfContents.Group>
<TableOfContents.Group label="Getting Started">
<TableOfContents.Item class-name="cursor-pointer">Installation</TableOfContents.Item>
<TableOfContents.Item class-name="cursor-pointer">Configuration</TableOfContents.Item>
</TableOfContents.Group>
<TableOfContents.Group label="API" href="#api-demo">
<TableOfContents.Item class-name="cursor-pointer">Props</TableOfContents.Item>
<TableOfContents.Item class-name="cursor-pointer">Events</TableOfContents.Item>
</TableOfContents.Group>
</TableOfContents.List>
</TableOfContents>
</div>
</template>Without title
The title sub-component is optional; use TableOfContents.List directly if you don't need a heading.
<script setup>
import { TableOfContents } from "@dicehub/phi/components/table-of-contents";
const headings = [
{ text: "Introduction" },
{ text: "Installation" },
{ text: "Usage" },
{ text: "API Reference" },
{ text: "Examples" },
];
</script>
<template>
<div class="min-w-48">
<TableOfContents>
<TableOfContents.List>
<TableOfContents.Item
v-for="heading in headings.slice(0, 3)"
:key="heading.text"
:active="heading.text === 'Introduction'"
class-name="cursor-pointer"
>
{{ heading.text }}
</TableOfContents.Item>
</TableOfContents.List>
</TableOfContents>
</div>
</template>Custom element
Use as to swap the default anchor for a button, router link, or any element.
<script setup>
import { ref } from "vue";
import { TableOfContents } from "@dicehub/phi/components/table-of-contents";
const clicked = ref(null);
</script>
<template>
<div class="min-w-48 space-y-3">
<TableOfContents>
<TableOfContents.List>
<TableOfContents.Item
as="button"
active
@click="clicked = 'Introduction'"
>
Introduction
</TableOfContents.Item>
<TableOfContents.Item
as="button"
@click="clicked = 'Installation'"
>
Installation
</TableOfContents.Item>
<TableOfContents.Item
as="button"
@click="clicked = 'Usage'"
>
Usage
</TableOfContents.Item>
</TableOfContents.List>
</TableOfContents>
<p v-if="clicked" class="text-xs" style="color: var(--phi-muted)">Clicked: {{ clicked }}</p>
</div>
</template>Vue Router
<template>
<TableOfContents.Item :as="RouterLink" to="/intro" active>
Introduction
</TableOfContents.Item>
</template>Nuxt
<template>
<TableOfContents.Item as="NuxtLink" to="/intro" active>
Introduction
</TableOfContents.Item>
</template>Button (no navigation)
<template>
<TableOfContents.Item as="button" @click="handleClick">
Introduction
</TableOfContents.Item>
</template>API Reference
TableOfContents
Root nav container with a default aria-label of Table of contents.
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | "Table of contents" | Vue prop alias for the root aria-label. |
default slot | slot | - | TableOfContents.Title and TableOfContents.List children. |
className | string | - | Additional CSS classes applied to the root nav. |
$attrs | HTMLAttributes<HTMLElement> | - | Native nav attributes. aria-label is supported. |
TableOfContents.Title
Optional uppercase heading displayed above the list. Renders a p.
| Prop | Type | Default | Description |
|---|---|---|---|
default slot | slot | - | Title content. |
className | string | - | Additional CSS classes applied to the p. |
$attrs | HTMLAttributes<HTMLParagraphElement> | - | Native paragraph attributes. |
TableOfContents.List
List container with a left border rail.
| Prop | Type | Default | Description |
|---|---|---|---|
default slot | slot | - | TableOfContents.Item and TableOfContents.Group children. |
className | string | - | Additional CSS classes applied to the ul. |
$attrs | HTMLAttributes<HTMLUListElement> | - | Native list attributes. |
TableOfContents.Item
Individual navigation item. Set active for the current section and as to customize the rendered element.
| Prop | Type | Default | Description |
|---|---|---|---|
active | boolean | false | Marks the item as current and sets aria-current="true". |
as | string | Component | "a" | Element or component to render as the item control. |
href | string | - | Anchor URL when rendering the default anchor. |
target | string | - | Anchor target. |
rel | string | - | Anchor rel attribute. |
default slot | slot | - | Item label. |
className | string | - | Additional CSS classes applied to the rendered item control. |
$attrs | AnchorHTMLAttributes | ButtonHTMLAttributes | - | Native attributes and listeners for the rendered item control. |
TableOfContents.Group
Groups items under a labeled section with indented children. Pass href to make the label a clickable link, or omit it for a plain title.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | Label displayed above the group's items. |
href | string | - | URL for a clickable group label. |
active | boolean | false | Marks a clickable group label as current and sets aria-current="true". |
default slot | slot | - | TableOfContents.Item children. |
className | string | - | Additional CSS classes applied to the group li. |
$attrs | HTMLAttributes<HTMLLIElement> | - | Native list item attributes. Click listeners target the label link when href is set. |
useTableOfContentsActiveId
SSR-safe active-section tracking for TableOfContents. Options accept plain values, refs, or getters.
| Prop | Type | Default | Description |
|---|---|---|---|
ids | MaybeRefOrGetter<readonly string[]> | - | Section anchor ids in document order. |
offset | MaybeRefOrGetter<number> | 0 | Activation-line offset from the top of the viewport or root. |
root | MaybeRefOrGetter<Element | null> | null | Custom scroll container. Defaults to the viewport. |
trackHash | MaybeRefOrGetter<boolean> | true | Selects matching location hashes on mount and hash changes. |
activeId | Readonly<Ref<string | null>> | null | Currently active section id. |
selectSection | (id: string) => void | - | Pins a selected section until scrolling settles. |