astro-toc-smol
v0.1.0
Published
Astro integration: generates TOC HTML from final rendered page at build time
Maintainers
Readme
astro-toc-smol
Astro integration that generates table-of-contents HTML from the final rendered page at build time, rather than extracting headings from MDX frontmatter or building the TOC in the browser with JavaScript.
Why
Astro's built-in heading extraction reads the MDX AST before components render, so headings generated by imported components, conditional blocks, or non-MDX content can be missed. Client-side TOC scripts fix coverage but add layout shift and depend on the DOM being ready. This plugin runs as a Vite transformIndexHtml hook after every framework component and layout has rendered, injecting the completed TOC directly into the HTML.
Installation
npm install astro-toc-smolSetup
// astro.config.ts
import astroToc from 'astro-toc-smol';
export default defineConfig({
integrations: [
astroToc(),
],
});Your layout must also render the placeholder nav when serverToc is true. If you use the FusionAuth docs TOC.astro component, pass serverToc={frontmatter.serverToc} and it handles this automatically. For custom layouts, render:
<nav id="toc-container" data-server-toc data-max-depth="4"></nav>The plugin finds this element, fills it with the generated TOC, and removes both data attributes before the page is written.
Usage
Add serverToc: true to any page's frontmatter:
---
title: My Page
serverToc: true
---No JavaScript, TOC is in the HTML when the page loads. A lightweight scroll-spy script (shipped by TOC.astro) handles the active-link highlighting at runtime.
Options
astroToc({
// CSS selector(s) for the content area to scan for headings.
// Tried in order; falls back to the full document if none match.
// Default: ['article.fusion-article section', 'article.fusion-article', 'article', 'main']
articleSelector: 'article.fusion-article section',
})articleSelector can be a string or an array of strings.
How it works
- A Vite
transformIndexHtmlhook runs on every generated HTML file. - If the page contains
data-server-toc, the HTML is parsed withnode-html-parser. - Headings (
h2–h6) and API endpoint markers ([data-toc-type="api"]) are collected from the article element in document order. - The same nested
<ul>structure used by the client-side TOC is built server-side and injected into the placeholder nav. - The placeholder attributes are removed and the modified HTML is returned.
TOC HTML structure
The generated markup mirrors TOC.astro's clientToc output so the same Tailwind classes and scroll-spy selectors work unchanged:
<ul id="toc-list" class="space-y-3 pt-5" data-widget="scroll-spy">
<li>
<div class="group" data-widget="scroll-spy-item">
<a href="#overview" class="block font-medium text-slate-600 text-sm ...">Overview</a>
</div>
<ul class="space-y-3 ml-4 pt-3" data-widget="scroll-spy">
<li>
<div class="group" data-widget="scroll-spy-item">
<a href="#sub-section" class="...">Sub Section</a>
</div>
</li>
</ul>
</li>
</ul>API headings (data-toc-type="api") are rendered with a coloured method badge:
<a href="#get-api-users" class="block font-mono text-xs ...">
<span class="font-bold ... text-yellow-600">GET</span>
<span>/api/users</span>
</a>Controlling depth
Set data-max-depth on the placeholder nav (or pass maxDepth to TOC.astro) to limit how many heading levels appear:
<nav id="toc-container" data-server-toc data-max-depth="3"></nav>Default is 4 (h2–h5).
