Components
- Accordion new
- ActionSheet new
- Alert new
- Alert Dialog new
- Angle Slider new
- Autocomplete new
- Avatar new
- Badge new
- Breadcrumb new
- Button new
- Button Group new
- Card new
- Carousel new
- Checkbox new
- Clipboard new
- Collapsible new
- Color Picker new
- Date Input new
- Date Picker new
- Dialog new
- DialogCaller new
- Drawer beta
- Editable new
- Empty new
- Field new
- Fieldset new
- File Upload new
- Filters new
- Floating Panel new
- Hover Card new
- Image new
- Image Cropper new
- Input new
- Input Group new
- Json Tree View new
- Kbd new
- Listbox new
- Marquee new
- Menu new
- Number Input new
- Pagination new
- Password Input new
- PhoneField new
- Pin Input new
- Popover new
- Progress Circular new
- Progress Linear new
- Qr Code new
- Radio Group new
- Rating Group new
- Scroll Area new
- ScrollSpy new
- Segment Group new
- Select new
- Separator new
- Sidebar new
- Signature Pad new
- Skeleton new
- Slider new
- Splitter new
- Steps new
- Switch new
- Table new
- Tabs new
- Tags Input new
- Textarea new
- Timeline new
- Timer new
- Toast new
- Toc new
- Toggle new
- Toggle Group new
- Tooltip new
- Tour new
- Tree View new
- Typography new
Features
- Active highlight — Primary mark on the rail for the current item
- Indicator modes — Pin the active item (
segment) or fill the rail as progress (fill) - Three rail styles — Continuous (
straight), 45° circuit (sharp), or S-curve circuit (rounded) - ScrollSpy aware — Reads active value from a parent ScrollSpy when present
- Depth indent —
depthon items offsets labels and the path rail - Zag.js machine — Headless state via
useToc/Toc.Provider
Installation
Install from the Vuzeno registry (includes ScrollSpy):
bunx --bun shadcn-vue@latest add https://vuzeno.com/r/toc.json
npx shadcn-vue@latest add https://vuzeno.com/r/toc.json
yarn dlx shadcn-vue@latest add https://vuzeno.com/r/toc.json
pnpm dlx shadcn-vue@latest add https://vuzeno.com/r/toc.json
Usage
<script setup lang="ts">
import { ScrollSpy } from "@/components/ui/scroll-spy";
import { Toc } from "@/components/ui/toc";
</script>
<template>
<ScrollSpy.Root>
<div class="flex gap-8">
<ScrollSpy.Viewport class="flex-1 overflow-y-auto">
<ScrollSpy.Item value="intro">
<h2 id="intro">Intro</h2>
</ScrollSpy.Item>
<ScrollSpy.Item value="usage">
<h2 id="usage">Usage</h2>
</ScrollSpy.Item>
</ScrollSpy.Viewport>
<Toc.Root turn="rounded">
<Toc.Title>On this page</Toc.Title>
<Toc.List>
<Toc.Indicator />
<Toc.Item value="intro">
<Toc.Link href="#intro">Intro</Toc.Link>
</Toc.Item>
<Toc.Item value="usage" :depth="3">
<Toc.Link href="#usage">Usage</Toc.Link>
</Toc.Item>
</Toc.List>
</Toc.Root>
</div>
</ScrollSpy.Root>
</template>
Composition
Toc.Root / Toc.Provider
├── Toc.Title
└── Toc.List
├── Toc.Indicator
└── Toc.Item
└── Toc.Link
Examples
Straight rail
turn="straight" keeps a continuous vertical line. The active item is highlighted as a primary segment on that line.
Sharp path
turn="sharp" draws a circuit that follows nested depth offsets with straight 45° diagonal turns.
Indicator modes
indicator="segment" (default) pins a short primary mark on the active item. indicator="fill" paints the rail from the start up to the active item — useful as reading progress. Toggle both modes and rail styles below.
API Reference
Toc.Root
| Prop | Type | Default |
|---|---|---|
turn | "straight" | "sharp" | "rounded" | "rounded" |
indicator | "segment" | "fill" | "segment" |
v-model:active-value | string | "" (used when outside ScrollSpy) |
useToc / Toc.Provider
Drive the machine imperatively and provide it to parts:
<script setup lang="ts">
import { Toc, useToc } from "@/components/ui/toc";
const toc = useToc({
turn: "rounded",
defaultActiveValue: "intro",
onActiveValueChange(details) {
console.log(details.value);
},
});
</script>
<template>
<Toc.Provider :value="toc">
<Toc.Title>On this page</Toc.Title>
<Toc.List>
<Toc.Indicator />
<Toc.Item value="intro">
<Toc.Link href="#intro">Intro</Toc.Link>
</Toc.Item>
</Toc.List>
</Toc.Provider>
</template>
Toc.Item
| Prop | Type | Default |
|---|---|---|
value | string | — |
depth | number | 2 |
Toc.Link
| Prop | Type | Default |
|---|---|---|
href | string | — |
Notes
- Prefer nesting Toc under ScrollSpy so the active section stays in sync automatically.
- Without ScrollSpy, drive the rail with
v-model:active-valueonToc.Root, or useuseToc+Toc.Provider. straightis a continuous rail;sharpandroundedfollow sub-item indents with 45° diagonals or S-curve turns. The active indicator rides that same path.indicator="fill"grows from the top of the rail to the active item;indicator="segment"only highlights the active item itself.