Panel-based table of contents
Quick reference
- To use it: add
toc_source: panelsto a page's frontmatter. - It swaps the right-rail TOC from markdown headings to
<Panel label="...">captions on that page only. - Unlabeled panels, and pages that don't set the flag, are unaffected.
- Source:
plugins/remark-panel-toc.mjs,src/components/Panel.
What it does
By default, Docusaurus builds a page's right-rail table of contents from its markdown headings (##, ###, …). That's a problem for a page that wraps several unrelated write-ups in <Panel label="..."> blocks - the TOC ends up listing every write-up's internal headings instead of the panels that actually separate the content.
Setting toc_source: panels in a page's frontmatter switches that page's TOC to list each <Panel label="..."> caption instead, one entry per labeled panel, linking straight to that panel.
---
title: Example page
toc_source: panels
---
Every other page keeps the normal heading-based TOC - this is opt-in per page, not a site-wide change.
No page currently opts into this. It was built for release-notes.mdx, which wrapped three unrelated samples in <Panel> blocks; that page was later split into three standalone pages (Sprint-aligned release notes, Customer-facing release notes, API release notes), each with its own normal heading-based TOC. The plugin and <Panel> component are left in place as available infrastructure for a future page that needs this pattern.
How it works
Docusaurus's MDX pipeline auto-generates export const toc = [...] for every page from its headings, but it explicitly leaves that export alone if the page already defines its own toc export (an existing Docusaurus feature meant for manually-authored TOCs). plugins/remark-panel-toc.mjs is a remark plugin, registered on the pages preset in docusaurus.config.js, that does this automatically instead of requiring anyone to hand-write the array:
- It no-ops unless the page's frontmatter has
toc_source: panels. - It walks the page for
<Panel label="...">elements. - For each one, it generates a slug from the label and stamps it onto the
<Panel>as anidattribute (unless the panel already has one), so the TOC link has something to scroll to. - It replaces the page's (already-generated)
tocexport with one entry per labeled panel.
Because the plugin runs after Docusaurus's own heading-based toc export already exists for the page, it only has to overwrite that export - it doesn't need to re-implement heading extraction itself.
src/components/Panel accepts the resulting id prop and renders it on its wrapping <div> so the anchor link resolves. You can also pass id by hand on a <Panel> for a stable anchor outside of this flow.
Notes
- Unlabeled panels (no
labelprop) never appear in the TOC, with or without this flag - there's nothing to show. - Headings inside a panel still render normally in the page body; they just stop contributing their own TOC entries once
toc_source: panelsis set. - If two panels on the same page share a label, the plugin appends
-2,-3, etc. to keep anchor ids unique.
Source
plugins/remark-panel-toc.mjs, src/components/Panel, the pages.remarkPlugins option in docusaurus.config.js.