The OnThisPage component renders a floating navigation panel listing the current document's headings, dynamically highlighting active sections as the user scrolls.
The OnThisPage primitive is style-neutral : it provides structure, the IntersectionObserver scroll-tracking, the active-track indicator math, and data-* state attributes. All visuals belong to your theme — colors, borders, indentation, the active highlight. Style states with Tailwind v4 variants (data-active:text-primary-500) or plain CSS ([data-active] { ... }).
Using the OnThisPage primitive modules, you can assemble a fully custom table of contents. The following example shows how to compose a layout with custom titles, list containers, activity indicator lines, and smooth scroll behaviors:
// docs/components/CustomTOC.tsx
import { useRef } from 'react'
import {
OnThisPage,
AnchorProvider,
ScrollProvider,
useActiveAnchors,
} from 'boltdocs/primitives'
import { useRoutes } from 'boltdocs/client'
export default function CustomTOC () {
const { currentRoute } = useRoutes ()
const scrollContainerRef = useRef < HTMLDivElement >( null )
const headings = currentRoute?.headings || []
if (headings. length === 0 ) return null
// Convert the headings database to a standard TOC structure
const tocItems = headings. map (( h ) => ({
title: h.text,
url: `#${ h . id }` ,
depth: h.level,
}))
return (
< OnThisPage className = "hidden xl:block w-toc py-4 pl-6 pr-4" >
{ /* 1. Static Title Header */ }
< OnThisPage.Header className = "text-xs font-bold uppercase tracking-wider text-muted mb-4" >
Table of Contents
</ OnThisPage.Header >
{ /* 2. Scroll Tracker Context Wrapper Providers */ }
< AnchorProvider toc = {tocItems} single = { false }>
< ScrollProvider containerRef = {scrollContainerRef}>
{ /* 3. Scrollable Viewport */ }
< OnThisPage.Content
ref = {scrollContainerRef}
className = "max-h-[80vh] pb-12"
fadeClassName = "bg-linear-to-b from-transparent to-main"
>
{ /* 4. List + active-track indicator */ }
< OnThisPage.List className = "relative border-l border-subtle space-y-0.5 text-sm" >
< OnThisPage.Indicator className = "-left-px w-0.5 rounded-full bg-primary-500" />
{headings. map (( h ) => (
< OnThisPage.Item
key = {h.id}
level = {h.level}
className = "data-[level=3]:pl-3"
>
< OnThisPage.Link
href = { `#${ h . id }` }
className = "block py-0.5 pl-4 text-[13px] transition-colors text-muted hover:text-body data-active:text-primary-500"
>
{h.text}
</ OnThisPage.Link >
</ OnThisPage.Item >
))}
</ OnThisPage.List >
</ OnThisPage.Content >
</ ScrollProvider >
</ AnchorProvider >
</ OnThisPage >
)
}
Expand code
[!TIP]
If you don't want to map the items manually, you can use the high-level primitive wrapper <OnThisPage.Tree headings={headings} /> to automatically mount the observers, indicators, and list links inside an active layout. Tree accepts className (list), itemClassName, linkClassName, indicatorClassName, contentClassName, and fadeClassName slots.
The OnThisPage primitives expose their state through data-* attributes, present only when true. Decide your own colors and treatments from CSS.
Attribute Emitted On Present When data-activeOnThisPage.Link anchorThe heading is the currently active section data-levelOnThisPage.ItemHeading hierarchy depth (e.g. 2, 3) — use data-[level=3]:pl-3 for indentation data-otp-rootOnThisPage.RootAlways data-otp-contentOnThisPage.ContentAlways data-otp-listOnThisPage.ListAlways data-otp-fadeContent bottom-fade element Always data-otp-indicatorOnThisPage.IndicatorAlways aria-currentOnThisPage.Link anchor"true" when active
< OnThisPage.Tree
headings = {headings}
className = "space-y-0.5 text-sm border-l border-subtle"
itemClassName = "data-[level=3]:pl-3"
linkClassName = "block py-0.5 pl-4 text-[13px] transition-colors text-muted hover:text-body data-active:text-primary-500"
indicatorClassName = "-left-px w-0.5 rounded-full bg-primary-500"
fadeClassName = "bg-linear-to-b from-transparent to-main"
/>
Expand code
The OnThisPage primitive provides the following subcomponents for structure customization:
Component HTML Tag Description Props OnThisPage.Root / OnThisPage<nav>The outer container wrapper representing the fixed desktop right-column navigation area. ComponentBasePropsOnThisPage.Header<div>A typographic title block for labeling the table of contents. ComponentBasePropsOnThisPage.Content<div>Scrollable viewport container (the scroll-spy boundary). OnThisPage.Content PropsOnThisPage.List<ul>List element wrapper wrapping the item anchors. ComponentBasePropsOnThisPage.Item<li>Individual list cell wrapper. Exposes data-level. OnThisPage.Item PropsOnThisPage.Link<a>Anchor link element intercepting click events to perform smooth viewport scrolling. OnThisPage.Link PropsOnThisPage.Indicator<div>A floating vertical highlight track indicator showing the active anchor link. ComponentBasePropsOnThisPage.ItemsVaries Loop helper rendering active links and indicators automatically based on heading settings. OnThisPage.Items PropsOnThisPage.Tree<nav>Automated high-level component grouping all providers, observers, and items. OnThisPage.Tree Props
Property Type Default Description childrenReactNodeundefinedChild content elements. classNamestringundefinedCustom utility CSS classes. styleCSSPropertiesundefinedInline style overrides.
Property Type Default Description refRef<HTMLDivElement>undefinedRef instance for the container element. scrollRefRefObject<HTMLElement>undefinedRef of the main viewport content element for tracking scroll. fadeClassNamestringundefinedClasses for the bottom fade element (theme-owned).
Property Type Default Description levelnumberundefinedThe heading hierarchy depth — exposed via data-level.
Property Type Default Description hrefstringRequired Target anchor id of the element (e.g., '#installation'). activebooleanfalseActive highlight override switch — exposed via data-active + aria-current. onClick(event) => voidundefinedCustom click interception callback.
Property Type Default Description headingsTOCItemType[]Required Raw page heading data to map. classNamestringundefinedClasses for the <ul>. itemClassNamestringundefinedClasses for each <li>. linkClassNamestringundefinedClasses for each <a> (style states via data-active). indicatorClassNamestringundefinedClasses for the active-track indicator.
Property Type Default Description headingsTOCItemType[]Required Headings array for active rendering. classNamestringundefinedClasses for the <ul> (passed through to Items). itemClassNamestringundefinedClasses for each <li>. linkClassNamestringundefinedClasses for each <a>. indicatorClassNamestringundefinedClasses for the active-track indicator. contentClassNamestringundefinedClasses for the scrollable content container. fadeClassNamestringundefinedClasses for the bottom fade element.
If you are building a fully custom tracker, you can import and wrap your layout in the custom React context providers exposed through OnThisPage:
Context Provider Description Props AnchorProviderObserves scroll events using IntersectionObserver and tracks the active page heading. AnchorProviderPropsScrollProviderAuto-scrolls the table of contents lists to keep the active item centered in the viewport. ScrollProviderProps
Property Type Default Description tocTOCItemType[]Required Table of contents headings array to observe. singlebooleanfalseWhen true, only activates one item at a time. observerOptionsIntersectionObserverInitundefinedIntersectionObserver options override.
Property Type Default Description containerRefRefObject<HTMLElement>Required Ref of the scroll container element holding the links list.
Last updated on October 18, 2026