N/A

Render an on-page table of contents from app-supplied heading links.

Preview state

Documentation article

Supply already-discovered headings from your app, then let PageToc render the aside and active-link treatment.

App owns

Markdown parsing, route paths, and page layout.

Component owns

Link list rendering, indentation, and active state display.

Installation

pnpm dlx shadcn-vue@latest add "https://ui.stackhacker.io/r/page-toc.json"

Usage

<script setup lang="ts">
import { PageToc, type PageTocItem } from "@/components/page-toc";

const headings: PageTocItem[] = [
  { id: "overview", title: "Overview", depth: 2 },
  { id: "installation", title: "Installation", depth: 2 },
  { id: "usage", title: "Usage", depth: 2 },
  { id: "examples", title: "Examples", depth: 3 },
];
</script>

<template>
  <PageToc :items="headings" class="sticky top-6" />
</template>

href defaults to #${id}. Pass a full path when your page shell needs route-aware anchors.

const headings: PageTocItem[] = [
  { id: "installation", title: "Installation", href: "/docs/components/page-toc#installation" },
];

App-Owned Headings

PageToc owns the visual table-of-contents list and active-link behavior for the data you pass in. Your app owns markdown parsing, heading extraction, page layout, and current route handling.

For Nuxt Content, map page.body.toc.links or another app-level source into PageTocItem[] before rendering the component.

import type { PageTocItem } from "@/components/page-toc";

const headings: PageTocItem[] = page.body.toc.links.flatMap(link => [
  {
    id: link.id,
    title: link.text,
    depth: link.depth,
  },
  ...(link.children ?? []).map(child => ({
    id: child.id,
    title: child.text,
    depth: child.depth,
  })),
]);

The component intentionally does not import Nuxt Content types, query content collections, inspect the current route, or create the page's aside column.

Active State

By default, PageToc observes matching heading elements by id in the browser and highlights the active link.

Use controlled active state when your page already knows the active heading, or when rendering a static preview.

<PageToc
  :items="headings"
  active-id="installation"
  :track-active="false"
/>

Examples

Default

Preview state

Documentation article

Supply already-discovered headings from your app, then let PageToc render the aside and active-link treatment.

App owns

Markdown parsing, route paths, and page layout.

Component owns

Link list rendering, indentation, and active state display.

API Reference

Props

PropTypeDefaultDescription
itemsPageTocItem[][]Heading links supplied by your app.
titlestring"On this page"Label rendered above the links. Use an empty string to hide it.
trackActivebooleantrueObserve matching page headings and update the active link.
activeIdstringControlled active heading id. Disables internal active tracking when supplied.
classHTMLAttributes["class"]Additional CSS classes for the root nav.

Types

interface PageTocItem {
  id: string;
  title: string;
  href?: string;
  depth?: number;
}