TableOfContents

Renders a list of headings on the page and tracks current heading visible in the viewport

Import

Usage

Use TableOfContents component to display table of contents like in the sidebar of mantine.dev documentation. The component tracks scroll position and highlights current heading in the list.

Variant
Color
Size
Radius
import { TableOfContents } from '@mantine/core';

function Demo() {
  return (
    <TableOfContents
      variant="filled"
      color="blue"
      size="sm"
      radius="sm"
      scrollSpyOptions={{
        selector: '#mdx :is(h1, h2, h3, h4, h5, h6)',
      }}
      getControlProps={({ data }) => ({
        onClick: () => data.getNode().scrollIntoView(),
        children: data.value,
      })}
    />
  );
}

use-scroll-spy options

TableOfContents in based on use-scroll-spy hook. You can pass options down to use-scroll-spy hook using scrollSpyOptions prop.

Example of customizing selector, depth and value retrieval:

import { TableOfContents } from '@mantine/core';

function Demo() {
  return (
    <TableOfContents
      scrollSpyOptions={{
        selector: '#mdx [data-heading]',
        getDepth: (element) => Number(element.getAttribute('data-order')),
        getValue: (element) => element.getAttribute('data-heading') || '',
      }}
    />
  );
}

Pass props to controls

You can pass props down to controls rendered by TableOfContents component with getControlProps function. It accepts an object with active and data properties and should return props object.

Example of changing controls to links:

import { TableOfContents } from '@mantine/core';

function Demo() {
  return (
    <TableOfContents
      getControlProps={({ active, data }) => ({
        component: 'a',
        href: `#${data.id}`,
        style: { color: active ? 'blue' : 'gray' },
        children: data.value,
      })}
    />
  );
}

Initial data

TableOfContents retrieves data on mount. If you want to render headings before TableOfContents component is mounted (for example during server-side rendering), you can pass initialData prop with array of headings data. initialData is replaced with actual data on mount.

import { TableOfContents } from '@mantine/core';

function Demo() {
  return (
    <TableOfContents
      initialData={[
        { id: '1', value: 'Heading 1', depth: 1 },
        { id: '2', value: 'Heading 2', depth: 2 },
        { id: '3', value: 'Heading 3', depth: 3 },
      ]}
    />
  );
}

Depth offset

Use minDepthToOffset prop to set minimum depth at which offset should be applied. By default, minDepthToOffset is 1, which means that first and second level headings will not be offset. Set it to 0 to apply offset to all headings.

To control offset value in px, set depthOffset prop:

import { TableOfContents } from '@mantine/core';

function Demo() {
  return (
    <TableOfContents
      minDepthToOffset={0}
      depthOffset={40}
      size="sm"
      scrollSpyOptions={{
        selector: 'h1, h2, h3, h4, h5, h6',
      }}
      getControlProps={({ data }) => ({
        onClick: () => data.getNode().scrollIntoView(),
        children: data.value,
      })}
    />
  );
}

autoContrast

TableOfContents supports autoContrast prop and theme.autoContrast. If autoContrast is set either on TableOfContents or on theme, content color will be adjusted to have sufficient contrast with the value specified in color prop.

Note that autoContrast feature works only if you use color prop to change background color. autoContrast works only with filled variant.

import { TableOfContents } from '@mantine/core';

function Demo() {
  return (
    <TableOfContents
      minDepthToOffset={0}
      depthOffset={40}
      size="sm"
      scrollSpyOptions={{
        selector: 'h1, h2, h3, h4, h5, h6',
      }}
      getControlProps={({ data }) => ({
        onClick: () => data.getNode().scrollIntoView(),
        children: data.value,
      })}
    />
  );
}

Styles API

Example of customizing TableOfContents with Styles API and data-* attributes:

import { TableOfContents } from '@mantine/core';
import classes from './Demo.module.css';

function Demo() {
  return (
    <TableOfContents
      size="sm"
      variant="none"
      classNames={classes}
      minDepthToOffset={0}
      depthOffset={40}
      scrollSpyOptions={{
        selector: 'h1, h2, h3, h4, h5, h6',
      }}
      getControlProps={({ data }) => ({
        onClick: () => data.getNode().scrollIntoView(),
        children: data.value,
      })}
    />
  );
}

Reinitialize

By default, TableOfContents does not track changes in the DOM. If you want to update headings data after the parent component has mounted, you can use reinitializeRef to get reinitialize function from use-scroll-spy hook:

import { useRef, useEffect } from 'react';
import { TableOfContents } from '@mantine/core';

function Demo({ dependency }) {
  const reinitializeRef = useRef(() => {});

  useEffect(() => {
    reinitializeRef.current();
  }, [dependency]);

  return <TableOfContents reinitializeRef={reinitializeRef} />;
}