A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes. Features a sliding indicator track that animates to the active heading. The list is a single tab stop: arrow keys move between headings, Home/End jump to the ends, and Enter/Space activate.
A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes. Features a sliding indicator track that animates to the active heading. The list is a single tab stop: arrow keys move between headings, Home/End jump to the ends, and Enter/Space activate.
Pass a flat ordered list of headings and let level control indentation.
Use activeId when custom scroll logic owns the active section.
Use density="compact" in dense sidebars where vertical space is tight.
Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content.
Pass scrollContainerRef when the content scrolls in a split pane, modal, or panel instead of the viewport.
Set offset to the height of a fixed header that overlays the content, so headings land below it instead of underneath it.
Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.
Rely on onNavigateEnd to mean "arrived" - it also fires when the user interrupts the scroll.
Typed props
Prop
Type and behavior
items
OutlineItem[] · required
Ordered heading items. Each item has id, label, and level (1-6). The id should match the target heading element id.
activeId
string
Currently active heading id. Providing this prop makes active state controlled and disables built-in scroll-spy.
onActiveIdChange
(id: string) => void
Called when the active item changes from built-in scroll-spy or from an outline link click.
label
string · default 'Table of contents'
Accessible label for the nav landmark.
density
'default' | 'compact' · default 'default'
Density variant controlling item padding. 'compact' for dense UIs, 'default' for standard spacing.
onNavigateStart
(id: string) => void
Called with the item id when navigation begins, before the scroll starts. Pair with onNavigateEnd to drive an arrival effect (flash, ring, pulse) on the target heading.
onNavigateEnd
(id: string) => void
Called with the item id once the navigation resolves - when the smooth scroll settles, or when reduced motion turns it into an instant jump. Fires exactly once per onNavigateStart, including when the user interrupts the scroll, so a "navigating" state can never leak.
offset
number · default 0
Height in px of a fixed header overlaying the top of the scroll root. Shifts both the activation line and the scroll landing by the same amount, so a heading activates exactly where navigating to it puts it - below the header, not underneath it. Composes with each heading's own scroll-margin-top (the header, then the breathing room below it); it does not replace it. Leave at 0 when nothing overlays the content and let scroll-margin-top do the work.
scrollContainerRef
React.RefObject<HTMLElement | null>
Scroll container to track, instead of auto-detecting the nearest scrollable ancestor. Use it when content scrolls inside a split pane, modal, or dashboard panel rather than the viewport.
hasScrollOnClick
boolean · default true
Whether activating an item smooth-scrolls to it. Set to false to own the scrolling yourself (virtualized content, a router): the Outline still updates the active item, the hash, and the navigate callbacks, but performs no scroll.
xstyle
StyleXStyles
StyleX styles for layout customization. Must be a stylex.create() value.
Anatomy
Outline · required
Navigation container for the same-page heading links and indicator.
Heading link · required
Anchor for one heading, indented by level and marked when active.