@tshewangrinzin/react-notion-toc
v0.1.0
Published
A Notion-style floating table of contents for React applications.
Maintainers
Readme
@tshewangrinzin/react-notion-toc
A Notion-style floating table of contents for React applications.
Install
npm install @tshewangrinzin/react-notion-tocImport the bundled stylesheet once in your app:
import "@tshewangrinzin/react-notion-toc/styles.css";FloatingToc
import { useRef } from "react";
import { FloatingToc } from "@tshewangrinzin/react-notion-toc";
import "@tshewangrinzin/react-notion-toc/styles.css";
export function ArticlePage() {
const contentRef = useRef<HTMLElement>(null);
return (
<>
<article ref={contentRef}>
<h1>Document Title</h1>
<h2>Overview</h2>
<h3>Details</h3>
<h2>Appendix</h2>
</article>
<FloatingToc contentRef={contentRef} />
</>
);
}Props
| Prop | Default | Notes |
| --- | --- | --- |
| contentRef | undefined | Scopes heading discovery to a container. Falls back to document. |
| headingSelector | "h1, h2, h3, h4" | Any selector that resolves to headings. |
| ariaLabel | "Table of Contents" | Label for the navigation landmark. |
| className | undefined | Applied to the floating wrapper. |
| top | 100 | Fixed offset from the top of the viewport on desktop. |
| right | 50 | Fixed offset from the right of the viewport. |
| mobileBottom | 20 | Bottom offset on screens <= 768px. |
| openDelay | 100 | Hover or focus delay before opening. |
| closeDelay | 0 | Delay before closing after pointer or focus exit. |
| scrollOffset | 100 | Offset applied when scrolling to a heading and when syncing active state. |
| smoothScroll | true | Uses smooth scrolling unless the user prefers reduced motion. |
| observeMutations | true | Rescans headings when the content tree changes. |
useFloatingToc
Use the hook when you want the heading data and interaction logic without the default UI.
import { useRef } from "react";
import { useFloatingToc } from "@tshewangrinzin/react-notion-toc";
export function CustomToc() {
const contentRef = useRef<HTMLElement>(null);
const { items, activeId, scrollToId } = useFloatingToc({ contentRef });
return (
<>
<article ref={contentRef}>{/* content */}</article>
<ul>
{items.map((item) => (
<li key={item.id}>
<button
type="button"
aria-current={item.id === activeId ? "true" : undefined}
onClick={() => scrollToId(item.id)}
>
{item.text}
</button>
</li>
))}
</ul>
</>
);
}The hook returns:
itemsactiveIdexpandedopen()close()toggle()scrollToId(id)
Styling
The stylesheet exposes CSS custom properties you can override on :root or a closer scope:
--rtnt-bg--rtnt-border--rtnt-text--rtnt-text-active--rtnt-line-inactive--rtnt-line-active--rtnt-radius--rtnt-shadow--rtnt-top--rtnt-right--rtnt-mobile-bottom--rtnt-font-family
