npx shadcn@latest add "https://crux-ui.vercel.app/r/toc.json"Wrap your page content with TOCProvider and pass an array of TOC items. Then place the PageTOC and PageTOCItems components in your sidebar.
import { type TOCItemType, TOCProvider, PageTOC, PageTOCItems } from "@/registry/abui/ui/toc"const tocItems: TOCItemType[] = [ { title: "Introduction", url: "#introduction", depth: 2 }, { title: "Getting Started", url: "#getting-started", depth: 2 }, { title: "Installation", url: "#installation", depth: 3 }, { title: "Configuration", url: "#configuration", depth: 3 }, { title: "Advanced Usage", url: "#advanced-usage", depth: 2 },]export default function Page() { return ( <TOCProvider toc={tocItems}> <div className="flex gap-8"> {/* Main content */} <div className="flex-1"> <section id="introduction">...</section> <section id="getting-started">...</section> <section id="installation">...</section> <section id="configuration">...</section> <section id="advanced-usage">...</section> </div> {/* TOC Sidebar */} <aside className="w-64 shrink-0"> <PageTOC className="sticky top-20"> <p className="mb-1 font-medium text-sm">On This Page</p> <PageTOCItems /> </PageTOC> </aside> </div> </TOCProvider> )}The clerk variant renders a depth-aware line that follows the hierarchy of your headings. This creates a visual connection between nested items.
<PageTOC className="sticky top-20"> <p className="mb-1 font-medium text-sm">On This Page</p> <PageTOCItems variant="clerk" /></PageTOC>A simple straight vertical line with depth-based indentation. The active indicator slides along the line to show the current position.
👉 Look at the TOC on the right side of this page - toggle between variants to see the difference!
A depth-aware line that changes horizontal position based on the heading depth. Creates a visual hierarchy with diagonal connectors between different depth levels.
0px10pxThe main context provider that manages active anchor state.
toc - TOCItemType[] - Array of TOC items (required)single - boolean - Only allow one active item at a time (default: false)children - ReactNode - Content to wrapRenders the TOC list with the selected variant style.
variant - "default" | "clerk" - Visual style variant (default: "default")emptyText - string - Text shown when no headings (default: "No Headings")The shape of each TOC item.
interface TOCItemType { title: ReactNode // The text to display url: string // The anchor URL (e.g., "#section-id") depth: number // Heading depth (2 = h2, 3 = h3, etc.)}The component exports several hooks for advanced usage:
useActiveAnchor() - Returns the first active anchor IDuseActiveAnchors() - Returns all visible anchor IDsuseTOCItems() - Returns the TOC items from contextimport { useActiveAnchor } from "@/registry/abui/ui/toc"function CurrentSection() { const activeAnchor = useActiveAnchor() return ( <div> Currently viewing: {activeAnchor || "none"} </div> )}