Marble UI
← Components

Table of Contents

Preview: Line

"On this page" list with scroll-spy: highlights the heading currently in view.

Import

import { TableOfContents } from '@marbleui/ui'

Props

<TableOfContents>

PropTypeDefaultDescription
items *requiredTocItem[]—
classNamestring—
rootElement | nullnullScroll container to observe (defaults to the viewport).
titlestring'On this page'
variant'plain' | 'line''line'

Usage notes

“On this page” list that highlights the heading currently in view. Use on long documents; for step progress use Stepper.

<TableOfContents
  items={[
    { id: 'install', title: 'Installation' },
    { id: 'usage', title: 'Usage' },
    { id: 'props', title: 'Props', level: 3 },
  ]}
/>
  • Each id must match an element id in the page; missing ones are skipped by the scroll-spy.
  • level is 2 (default) or 3 for indentation. variant is line (default) or plain.
  • If the content scrolls in its own container rather than the window, pass that element as root.
  • title (default “On this page”) is both the visible heading and the nav’s aria-label. Links are plain #id anchors.
Example source · show
import { useState } from 'react'
import { TableOfContents } from './table-of-contents'

const items = [
  { id: 'toc-intro', title: 'Introduction' },
  { id: 'toc-install', title: 'Installation' },
  { id: 'toc-npm', title: 'With npm', level: 3 as const },
  { id: 'toc-registry', title: 'With the registry', level: 3 as const },
  { id: 'toc-usage', title: 'Usage' },
  { id: 'toc-faq', title: 'FAQ' },
]

function Demo({ variant }: { variant: 'line' | 'plain' }) {
  const [root, setRoot] = useState<HTMLDivElement | null>(null)
  return (
    <div className="flex w-[36rem] gap-6">
      <div ref={setRoot} tabIndex={0} role="region" aria-label="Scrollable example" className="h-64 flex-1 overflow-y-auto outline-none focus-visible:ring-2 focus-visible:ring-ring  rounded-lg border border-border bg-surface p-4 text-sm text-fg-muted">
        {items.map((i) => (
          <section key={i.id} className="mb-6">
            <h3 id={i.id} className={i.level === 3 ? 'font-medium text-fg' : 'text-base font-semibold text-fg'}>{i.title}</h3>
            <p className="mt-2">Scroll this panel: the list on the right follows along. Section text for “{i.title}”.</p>
            <p className="mt-2">More content so each section takes up some space in the scroll container.</p>
          </section>
        ))}
      </div>
      {root && <TableOfContents items={items} variant={variant} root={root} className="w-44" />}
    </div>
  )
}

export const styles = { Line: () => <Demo variant="line" />, Plain: () => <Demo variant="plain" /> }

Source: packages/ui/src/table-of-contents/table-of-contents.tsx

Catalog

Category
navigation
Priority
Core
Primitive
composed
React Aria
Link