import { TableOfContents } from "@cloudflare/kumo";
export function TableOfContentsBasicDemo() {
return (
<DemoWrapper>
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
{headings.map((heading) => (
<TableOfContents.Item
key={heading.text}
active={heading.text === "Usage"}
className="cursor-pointer"
>
{heading.text}
</TableOfContents.Item>
))}
</TableOfContents.List>
</TableOfContents>
</DemoWrapper>
);
} Installation
Barrel
import { TableOfContents } from "@cloudflare/kumo";Granular
import { TableOfContents } from "@cloudflare/kumo/components/table-of-contents"; Usage
import { TableOfContents } from "@cloudflare/kumo";
export default function Example() {
return (
<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>
);
}This component is purely presentational. All interaction logic — scroll
tracking, IntersectionObserver, active state management — is left to the
consumer.
Examples
Interactive
Click an item to set it as active. The consumer controls state via active
and onClick.
import { useState } from "react";
import { TableOfContents } from "@cloudflare/kumo";
export function TableOfContentsInteractiveDemo() {
const [active, setActive] = useState("Introduction");
return (
<DemoWrapper>
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
{headings.map((heading) => (
<TableOfContents.Item
key={heading.text}
active={heading.text === active}
onClick={() => setActive(heading.text)}
className="cursor-pointer"
>
{heading.text}
</TableOfContents.Item>
))}
</TableOfContents.List>
</TableOfContents>
</DemoWrapper>
);
} No active item
When no item has active set, all items show the default subtle text style
with a hover indicator.
import { TableOfContents } from "@cloudflare/kumo";
export function TableOfContentsNoActiveDemo() {
return (
<DemoWrapper>
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
{headings.map((heading) => (
<TableOfContents.Item key={heading.text} className="cursor-pointer">
{heading.text}
</TableOfContents.Item>
))}
</TableOfContents.List>
</TableOfContents>
</DemoWrapper>
);
} Groups
Use TableOfContents.Group to organize items into labeled sections with
indented children. Groups support two modes: pass an href to make the group
label a clickable link (like “Examples” and “API” below), or omit it for a
plain non-interactive title (like “Getting Started”).
import { TableOfContents } from "@cloudflare/kumo";
/** Shows both group modes: clickable group labels (with `href`) and plain title labels (without `href`). */
export function TableOfContentsGroupDemo() {
return (
<DemoWrapper>
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
<TableOfContents.Item active className="cursor-pointer">
Overview
</TableOfContents.Item>
<TableOfContents.Group label="Examples" href="#examples-demo">
<TableOfContents.Item className="cursor-pointer">
Basic example
</TableOfContents.Item>
<TableOfContents.Item className="cursor-pointer">
Advanced example
</TableOfContents.Item>
</TableOfContents.Group>
<TableOfContents.Group label="Getting Started">
<TableOfContents.Item className="cursor-pointer">
Installation
</TableOfContents.Item>
<TableOfContents.Item className="cursor-pointer">
Configuration
</TableOfContents.Item>
</TableOfContents.Group>
<TableOfContents.Group label="API" href="#api-demo">
<TableOfContents.Item className="cursor-pointer">
Props
</TableOfContents.Item>
<TableOfContents.Item className="cursor-pointer">
Events
</TableOfContents.Item>
</TableOfContents.Group>
</TableOfContents.List>
</TableOfContents>
</DemoWrapper>
);
} Without title
The title sub-component is optional — use TableOfContents.List directly if
you don’t need a heading.
import { TableOfContents } from "@cloudflare/kumo";
export function TableOfContentsWithoutTitleDemo() {
return (
<DemoWrapper>
<TableOfContents>
<TableOfContents.List>
{headings.slice(0, 3).map((heading) => (
<TableOfContents.Item
key={heading.text}
active={heading.text === "Introduction"}
className="cursor-pointer"
>
{heading.text}
</TableOfContents.Item>
))}
</TableOfContents.List>
</TableOfContents>
</DemoWrapper>
);
} Custom element
Use the render prop to swap the default anchor for a button, router link, or
any element.
import { useState } from "react";
import { TableOfContents } from "@cloudflare/kumo";
/** Demonstrates using the `render` prop with a custom link component. */
export function TableOfContentsRenderPropDemo() {
const [clicked, setClicked] = useState<string | null>(null);
return (
<DemoWrapper>
<div className="space-y-3">
<TableOfContents>
<TableOfContents.List>
{["Introduction", "Installation", "Usage"].map((text) => (
<TableOfContents.Item
key={text}
render={<button type="button" />}
onClick={() => setClicked(text)}
active={text === "Introduction"}
>
{text}
</TableOfContents.Item>
))}
</TableOfContents.List>
</TableOfContents>
{clicked && (
<p className="text-xs text-kumo-subtle">Clicked: {clicked}</p>
)}
</div>
</DemoWrapper>
);
} React Router
<TableOfContents.Item render={<Link to="/intro" />} active>
Introduction
</TableOfContents.Item>Next.js
import Link from "next/link";
<TableOfContents.Item render={<Link href="/intro" />} active>
Introduction
</TableOfContents.Item>;Button (no navigation)
<TableOfContents.Item render={<button type="button" />} onClick={handleClick}>
Introduction
</TableOfContents.Item> API Reference
TableOfContents
Root nav container with a default aria-label of “Table of contents”.
| Prop | Type | Default |
|---|---|---|
| children | ReactNode | - |
| className | string | - |
| id | string | - |
| lang | string | - |
| title | string | - |
TableOfContents.Title
Optional uppercase heading displayed above the list (renders a <p>).
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.
TableOfContents.List
List container with a left border rail.
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.
TableOfContents.Item
Individual navigation link. Set active for the current section. Use the
render prop to swap the anchor for a router link or button.
| Prop | Type | Default |
|---|---|---|
| active | boolean | - |
TableOfContents.Group
Groups items under a labeled section with indented children. Pass href to make the label a clickable link, or omit for a plain title.
| Prop | Type | Default |
|---|---|---|
| label* | string | - |
| href | string | - |
| active | boolean | - |