import { TableOfContents } from "@cloudflare/kumo";
export function TableOfContentsBasicDemo() {
return (
<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>
);
} 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 (
<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>
);
} 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 (
<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>
);
} Groups
Use TableOfContents.Group to organize items into labeled sections with
indented children.
import { TableOfContents } from "@cloudflare/kumo";
export function TableOfContentsGroupDemo() {
return (
<TableOfContents>
<TableOfContents.Title>On this page</TableOfContents.Title>
<TableOfContents.List>
<TableOfContents.Item active className="cursor-pointer">
Overview
</TableOfContents.Item>
<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">
<TableOfContents.Item className="cursor-pointer">
Props
</TableOfContents.Item>
<TableOfContents.Item className="cursor-pointer">
Events
</TableOfContents.Item>
</TableOfContents.Group>
</TableOfContents.List>
</TableOfContents>
);
} 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 (
<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>
);
} API Reference
| Prop | Type | Default |
|---|---|---|
| children | ReactNode | - |
| className | string | - |
| id | string | - |
| lang | string | - |
| title | string | - |