Orientation
Content Processing
Search Infrastructure
Rendering and UI
Developer Tooling
How It Works
The following files were used as context for generating this wiki page:
The Table of Contents (TOC) subsystem in Fumadocs is a highly reactive, scroll-aware navigation component designed to synchronize with the page's DOM state. It bridges the gap between static content generation (MDX parsing) and dynamic UI feedback, allowing users to track their current position in long-form documentation seamlessly.
The subsystem operates through a multi-layered architecture: a core observation layer that detects intersection changes, a transformation layer that translates these observations into visual state, and a UI layer that maps headings to interactive navigation items. By utilizing IntersectionObserver coupled with a custom notification system, the TOC ensures high-performance tracking without manual scroll listeners.
This system effectively solves the "floating navigation" problem in complex technical documentation, where users need context-aware markers that stay updated as they navigate. Its modularity permits various visual implementations (like the "clerk" style vs. the "default" style) while relying on a unified set of reactive primitives for the underlying logic.
The core of the TOC logic resides in the Observer class located in packages/core/src/toc.tsx. This class manages the state of all tracked items and orchestrates the IntersectionObserver instances. When setItems is called, it reconciles the provided items list with the DOM, clearing existing observers and initializing new ones on the corresponding heading elements.
The callback method is the heart of the synchronization loop. It processes incoming intersection entries, maintaining an active state for each heading. The system handles scenarios where no headings are intersecting by defaulting to the nearest heading in the viewport, which prevents the TOC from showing an empty state during rapid scrolling.
Sources: packages/core/src/toc.tsx:282-331
Note
The Observer class uses a Map-like approach internally to maintain a Set of ChangeListener listeners. Whenever the internal item list changes via update, all registered listeners are notified, enabling UI components to re-render reactively.
While the core package handles state, UI packages (like base-ui and radix-ui) transform this state into visual representation using SVGs and CSS variables. The TOCItems component calculates the layout of the TOC path, creating a dynamic visual "track" that follows the current scroll position.
The calculation logic in default.tsx maps the nesting depth of headings (depth) to horizontal offsets, ensuring a tree-like visual indentation. These offsets are derived using getLineOffset and getItemOffset functions, which transform the hierarchical document structure into a renderable SVG path.
Sources: packages/base-ui/src/components/toc/default.tsx:240-250
The TOC subsystem relies on React Context to expose the observer and the scroll state to nested items. The AnchorProvider wraps the content, providing an ObserverContext, while the ScrollProvider provides the containerRef, necessary for the scrollIntoView logic when a user clicks an item.
Sources: packages/core/src/toc.tsx:38-83
To support static generation or server-side rendering contexts (like remark), the subsystem includes plugins such as remark-structure and rehype-toc. These plugins parse the MDX tree to extract headings and their metadata before the component is even rendered on the client.
The rehype-toc plugin specifically scans heading tags and generates an array of items, which can then be exported as an ESM variable (toc) or embedded in the file's metadata. This ensures that the TOC doesn't require a full re-parse on every client render if the structure is known at build time.
Important
The rehype-toc plugin requires hProperties.id to be present on headings. If headings lack IDs, the TOC logic cannot reliably track them; remark-heading should be used as a prerequisite plugin to ensure every heading has a stable, slugified ID.
Sources: packages/core/src/mdx-plugins/rehype-toc.ts:55-108
The architecture reflects a preference for performance and visual flexibility over a single-component solution.
Sources: packages/core/src/toc.tsx:228-346
To use the TOC in a document layout, wrap the content in the TOCProvider and render the TOC component. The following snippet illustrates how a developer integrates the TOC into a layout:
import { TOCProvider, TOC } from 'fumadocs-ui/components/toc';
export function DocsPage({ toc, children }) {
return (
<TOCProvider toc={toc}>
<main>{children}</main>
<TOC />
</TOCProvider>
);
}Sources: packages/preview/src/pages/[...slugs].tsx:212-221
Tip
Always place the TOCProvider as high as possible in the document component tree to ensure all TOCItem components receive the updated context immediately upon mount.