## TableOfContents Renders a list of headings on the page and tracks current heading visible in the viewport Category: Navigation ### Usage Use the `TableOfContents` component to display a table of contents similar to the sidebar in these docs. The component tracks scroll position and highlights current heading in the list. You can set the style of the `TableOfContents` items (controls) with the `variant`, `color`, `size` and `radius` props: ### Selector `TableOfContents` is based on Mantine’s [`use-scroll-spy`](https://v8.mantine.dev/hooks/use-scroll-spy/) hook. The `selector` prop is passed directly to this hook. The `selector` prop is a CSS selector string used to locate and observe heading elements in the DOM. The default value is `'h1, h2, h3, h4, h5, h6'`. ```python dmc.TableOfContents( selector="h1, h2, h3, h4, h5, h6", # default ) ``` The selector can be scoped to a container and may use any valid CSS selector syntax, including `:is()`, class selectors, or IDs. For example, the following selector matches `h2`, `h3`, and `h4` elements in the `AppShellMain` component, which has the class name `mantine-AppShell-main`: ```python dmc.TableOfContents( selector=".mantine-AppShell-main :is(h2, h3, h4)" ) ``` ### Controls The `TableOfContents` items (controls) are rendered as HTML `` elements. Each control’s `href` attribute is the id of a heading element, and its `children` are set to the heading element’s `textContent`. The active control (the currently visible heading) includes a `data-active="true"` attribute, which can be used for styling or testing. ### Depth offset Use the `minDepthToOffset` prop to control the minimum heading depth at which indentation is 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: ```python import dash_mantine_components as dmc component = dmc.TableOfContents( selector=".mantine-AppShell-main :is( h2, h3, h4)", variant="filled", color="blue", size="sm", radius="sm", minDepthToOffset=0, depthOffset=40, w=300 ) ``` ### 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. ```python import dash_mantine_components as dmc component = dmc.TableOfContents( selector=".mantine-AppShell-main :is( h2, h3, h4)", variant="filled", color="yellow.3", autoContrast=True, size="sm", radius="sm", w=300 ) ``` ### Reinitialize By default, heading changes are not tracked automatically. If the content updates in a callback (for example, when switching tabs) you can trigger a refresh of the `TableOfContents` by setting `reinitialize=True` in a callback. ```python from dash import Dash, html from dash import Input, Output, callback import dash_mantine_components as dmc app = Dash() def make_section(i, name): return dmc.Box( [ dmc.Title(children=f"{name} {i}", id=f"section-{i}", order=3), dmc.Space(h=200), ], p="lg", ) app.layout = dmc.MantineProvider( dmc.AppShell( dmc.AppShellMain( [ dmc.Tabs( [ dmc.TabsList( [ dmc.TabsTab("Tab one", value="1"), dmc.TabsTab("Tab two", value="2"), ] ), ], value="1", id="tabs", ), dmc.Box(id="tabs-content"), dmc.AppShellAside( [ dmc.Title("Table of Contents", order=4, mt=60), dmc.ScrollArea( dmc.TableOfContents( selector="#tabs-content :is( h2, h3, h4)", id="toc" ) ), ] ), ], p="md", ) ) ) @callback( Output("tabs-content", "children"), Output("toc", "reinitialize"), Input("tabs", "value"), ) def render_content(active): if active == "1": return html.Div([make_section(i, "Section") for i in range(30)]), True return html.Div([make_section(i, "Topic") for i in range(30)]), True if __name__ == "__main__": app.run(debug=True) ``` ### target_id With Dash 3+, you do not need to set `reinitialize` in a callback. Instead, set `target_id` to the `id` of a component that is updated by callbacks, and the `TableOfContents` will refresh automatically when that update completes. Using the example above, the callback can be simplified. No `reinitialize` output is required when `target_id` is set: ```python from dash import Dash, html from dash import Input, Output, callback import dash_mantine_components as dmc app = Dash() def make_section(i, name): return dmc.Box( [ dmc.Title(children=f"{name} {i}", id=f"section-{i}", order=3), dmc.Space(h=200), ], p="lg", ) app.layout = dmc.MantineProvider( dmc.AppShell( dmc.AppShellMain( [ dmc.Tabs( [ dmc.TabsList( [ dmc.TabsTab("Tab one", value="1"), dmc.TabsTab("Tab two", value="2"), ] ), ], value="1", id="tabs", ), dmc.Box(id="tabs-content"), dmc.AppShellAside( [ dmc.Title("Table of Contents", order=4, mt=60), dmc.ScrollArea( dmc.TableOfContents( target_id="tabs-content", selector="#tabs-content :is( h2, h3, h4)", ) ), ] ), ], p="md", ) ) ) @callback( Output("tabs-content", "children"), Input("tabs", "value"), ) def render_content(active): if active == "1": return html.Div([make_section(i, "Section") for i in range(30)]) return html.Div([make_section(i, "Topic") for i in range(30)]) if __name__ == "__main__": app.run(debug=True) ``` When using Dash Pages, `target_id` defaults to the page container id, so the table of contents is automatically refreshed on each page change without any additional configuration. ### Dash version support * Dash 3+: `target_id` is supported and replaces the `reinitialize` callback pattern. * Dash 2: Use the `reinitialize` prop in a callback as shown above. ### scrollIntoViewOptions `scrollIntoViewOptions` prop controls how the page scrolls when a heading is clicked in the `TableOfContents`. This prop is passed directly to the browser’s [`Element.scrollIntoView`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) API and lets you customize the scrolling behavior and alignment of the target heading. For example, you can enable smooth scrolling or control where the heading appears in the viewport: ```python dmc.TableOfContents( scrollIntoViewOptions={ "behavior": "smooth", "block": "start", } ) ``` ### Apps with fixed headers This example shows how to handle scrolling when the app has a fixed header, such as when using `AppShellHeader` - The `scrollMarginTop` CSS property is applied to headings so that when a `TableOfContents` item is clicked, the heading is not hidden behind the header. - The `offset` prop is passed to the `useScrollSpy` hook to track the correct heading when the app has a fixed header. Set it to the header height so the active item in the `TableOfContents` updates properly when scrolling. ```python import dash from dash import Dash, html import dash_mantine_components as dmc app = Dash(use_pages=True, pages_folder="") logo = "https://github.com/user-attachments/assets/c1ff143b-4365-4fd1-880f-3e97aab5c302" def make_section(i, page): return dmc.Box( [ dmc.Title( children=f"{page} Title {i}", id=str(i), order=3, mb=30, style={"scrollMarginTop": "60px"}, ), ], p="lg", ) header = dmc.AppShellHeader( dmc.Group( [ dmc.Image(src=logo, h=40, flex=0), dmc.Title("Demo App", c="blue"), ], h="100%", px="md", ) ) aside = dmc.AppShellAside( children=dmc.ScrollArea( dmc.Stack( [ dmc.Title("Table of contents", order=5, mt=50), dmc.TableOfContents( variant="filled", color="blue", size="sm", radius="sm", selector="#appshellmain :is( h2, h3, h4, h5, h6)", offset=60, id="toc", ), ] ), type="never", ), px="lg", ) dash.register_page( "home", path="/", layout=html.Div([make_section(i, "home") for i in range(30)]), ) dash.register_page( "page1", path="/page-1", layout=html.Div([make_section(i, "page1") for i in range(30)]), ) app.layout = dmc.MantineProvider( children=dmc.AppShell( [ header, dmc.AppShellNavbar( [ dmc.Title("Page Links", order=5), dmc.NavLink(label="Home", href="/", id="home"), dmc.NavLink(label="Page 1", href="/page-1", id="page-1"), ], p="md", ), dmc.AppShellMain(html.Div(dash.page_container, id="appshellmain")), aside, ], navbar={"width": 200}, header={"height": 60}, ), ) if __name__ == "__main__": app.run(debug=True) ``` ### Styles API This component supports Styles API. With Styles API, you can customize styles of any inner element. See the Styling and Theming sections of these docs for more information. #### TableOfContents selectors | Selector | Static selector | Description | | -------- | ---------------------------------- | --------------- | | root | `.mantine-TableOfContents-root` | Root element | | control | `.mantine-TableOfContents-control` | Control element | #### TableOfContents CSS variables | Selector | Variable | Description | | -------- | -------------------- | ---------------------------------------------- | | root | `--toc-bg` | Background color of active control | | root | `--toc-color` | Text color of active control | | root | `--toc-depth-offset` | Offset between controls depending on depth | | root | `--toc-radius` | Border-radius of control | | root | `--toc-size` | Controls font-size and padding of all elements | #### TableOfContents data attributes | Selector | Attribute | Condition | | -------- | ------------- | -------------------------------------------------------------- | | control | `data-active` | Associated heading is currently the best match in the viewport | ### Keyword Arguments #### TableOfContents - id (string; optional): Unique ID to identify this component in Dash callbacks. - aria-* (string; optional): Wild card aria attributes. - attributes (boolean | number | string | dict | list; optional): Passes attributes to inner elements of a component. See Styles API docs. - autoContrast (boolean; optional): Determines whether text color with filled variant should depend on `background-color`. If luminosity of the `color` prop is less than `theme.luminosityThreshold`, then `theme.white` will be used for text color, otherwise `theme.black`. Overrides `theme.autoContrast`. - className (string; optional): Class added to the root element, if applicable. - classNames (dict; optional): Adds custom CSS class names to inner elements of a component. See Styles API docs. - color (optional): Active element color. Key of `theme.colors` or any valid CSS color value, `theme.primaryColor` by default. - darkHidden (boolean; optional): Determines whether component should be hidden in dark color scheme with `display: none`. - data-* (string; optional): Wild card data attributes. - depthOffset (string | number; optional): Controls padding on the left side of control, multiplied by (`depth` - `minDepthToOffset`), `20px` by default. - hiddenFrom (string; optional): Breakpoint above which the component is hidden with `display: none`. - lightHidden (boolean; optional): Determines whether component should be hidden in light color scheme with `display: none`. - loading_state (dict; optional): Object that holds the loading state object coming from dash-renderer. For use with dash<3. `loading_state` is a dict with keys: - minDepthToOffset (number; optional): Minimum `depth` value that requires offset, `1` by default. - mod (string | dict | list of string | dicts; optional): Element modifiers transformed into `data-` attributes. For example: "xl" or {"data-size": "xl"}. Can also be a list of strings or dicts for multiple modifiers. Falsy values are removed. - offset (number; optional): Offset from the top of the viewport to use when determining the active heading, 0 by default. - radius (number; optional): Key of `theme.radius` or any valid CSS value to set `border-radius`, `theme.defaultRadius` by default. - reinitialize (boolean; optional): Forces a re-scan of headings for dynamic content. Can be triggered in a callback. - scrollIntoViewOptions (dict; optional): Set scrollIntoView options. {'behavior': 'auto' | 'instant' | 'smooth', 'block': 'center' | 'end' | 'nearest' | 'start', 'inline': 'center' | 'end' | 'nearest' | 'start'}. `scrollIntoViewOptions` is a dict with keys: - selector (string; optional): CSS Selector to get headings, 'h1, h2, h3, h4, h5, h6' by default. - size (string | number; optional): Controls font-size and padding of all elements, `'md'` by default. - styles (boolean | number | string | dict | list; optional): Adds inline styles directly to inner elements of a component. See Styles API docs. - tabIndex (number; optional): tab-index. - target_id (string; optional): Component id to observe for loading completion (Dash >= 3 only). Defaults to Dash Pages content container '_pages_content'. For Dash 2 use reinitialize prop instead. - variant (a value equal to: 'none', 'light', 'filled'; optional): Controls active element style, `'filled'` by default. - visibleFrom (string; optional): Breakpoint below which the component is hidden with `display: none`.