useActiveHeadingId
function useActiveHeadingId(options: ActiveHeadingIdOptions): string;
The useActiveHeadingId
hook can be used to determine which heading group is
visible within the viewport and normally used with a scrollable table of
contents. This hook is heavily inspired by the
Mozilla Docs Table of Contents
behavior and is used for this website's Table of Contents view on non-mobile devices.
Example Usage
The useActiveHeadingId
requires providing a list of heading ids for the page
to enable creating intersection observer events when the heading is scrolled
into view. It is recommended to use this hook with
mdx/rehype but can be
used for other applications as well. Here's a small example for adding a
dynamic table of contents in a client-side app.
Check out this website's table of contents and rehype-toc implementation to see how it can integrate into MDX and rehype.
"use client";
import { useSsr } from "@react-md/core/SsrProvider";
import { type HeadingReferenceWithChildren } from "@react-md/core/navigation/types";
import { useActiveHeadingId } from "@react-md/core/navigation/useActiveHeadingId";
import { useTableOfContentsHeadings } from "@react-md/core/navigation/useTableOfContentsHeadings";
import { Typography } from "@react-md/core/typography/Typography";
import { RenderRecursively } from "@react-md/core/utils/RenderRecursively";
import { useId } from "react";
import styles from "./TableOfContents.module.scss";
export function TableOfContents() {
const headingId = useId();
const ssr = useSsr();
const headings = useTableOfContentsHeadings();
const activeHeadingId = useActiveHeadingId({ headings });
return (
<nav aria-labelledby={headingId} className={styles.container}>
<Typography id={headingId} type="headline-5" margin="none">
Table of Contents
</Typography>
<TableOfContentsGroup root>
<RenderRecursively
data={activeHeadingId}
items={transformToItems(toc)}
render={RenderTableOfContentsItem}
getItemKey={({ item }) => item.id}
/>
</TableOfContentsGroup>
</nav>
);
}
Parameters
options
- An object with the following definition:
export interface ActiveHeadingIdOptions {
headings: readonly HeadingReferenceWithChildren[];
/** @see {@link DEFAULT_ACTIVE_HEADING_THRESHOLD} */
threshold?: IntersectionObserverThreshold;
/** @see {@link DEFAULT_ACTIVE_HEADING_GET_ROOT_MARGIN} */
getRootMargin?: () => IntersectionObserverRootMargin;
/** @defaultValue `headings[0]?.id ?? ""` */
defaultActiveId?: UseStateInitializer<string>;
/** @defaultValue `0.8` */
scrollBottomThreshold?: number;
}
export interface HeadingReference {
id: string;
}
export interface HeadingReferenceWithChildren extends HeadingReference {
children?: readonly HeadingReferenceWithChildren[];
}
Returns
The id
for the heading that is currently "active" by having content mostly visible within the viewport.