astro-toc

A flexible Table of Contents (ToC) generator component for Astro. Perfect for blogs, documentation, or any content-rich page which benefits from in-page navigation.
Note: In this documentation:
TOC
refers to the Astro component (<TOC />
)
ToC
refers to the data or concept of a Table of Contents
Table of Contents
Features
- 🧩 Flexible: Use custom component for enhanced render output
- 📐 Fine-tune: Use
depth
and maxDepth
props to control nested level range
- 🗂️ Semantic output: Uses native
ul
, ol
, or menu
elements
- ⚙️ TypeScript friendly
Demo
Try it out on:

Installation
Choose your favorite package manager and install astro-toc
.
npm install astro-toc
pnpm add astro-toc
yarn add astro-toc
bun add astro-toc
Usage
Provide your ToC
data as an array of TocItem
objects (each requiring depth
and title
, with optional url
). Pass this data to the TOC
component for rendering.
Basic example
---
import type { TocItem } from "astro-toc";
const toc = [
{ depth: 1, title: "Main Stage - Friday", url: "#main-stage-friday" },
{ depth: 2, title: "The Rock Lobsters (8pm)", url: "#the-rock-lobsters" },
{ depth: 2, title: "Cosmic Latte (10pm)", url: "#cosmic-latte" },
{ depth: 1, title: "Dance Tent - Friday", url: "#dance-tent" },
{ depth: 2, title: "DJ SpinCycle (9pm)", url: "#dj-spincycle" },
{ depth: 2, title: "GlitterBomb (11pm)", url: "#glitterbomb" },
{ depth: 1, title: "Main Stage - Saturday", url: "#main-stage-saturday" },
{ depth: 2, title: "The Acousticats (7pm)", url: "#the-acousticats" },
{ depth: 2, title: "Planet Groove (9pm)", url: "#planet-groove" },
{ depth: 1, title: "Food & Merch", url: "#food-merch"},
{ depth: 2, title: "Vegan Delights", url: "#food-vegan"},
{ depth: 2, title: "Band T-Shirts", url: "#merch-shirts"},
] satisfies TocItem[];
---
<TOC toc={toc} />
Custom component example
Implementing a custom
component allows you to create a richer, more styled ToC output. Extend the TocItem
with extra props and use them in your component.
---
import type { TocItem } from "astro-toc";
import Card, { type CardProps } from "@/components/Card.astro";
const toc = [
{ depth: 1, title: "Main Stage - Friday", url: "#main-stage-friday", icon: "location" },
{ depth: 2, title: "The Rock Lobsters (8pm)", url: "#the-rock-lobsters", icon: "microphone" },
{ depth: 2, title: "Cosmic Latte (10pm)", url: "#cosmic-latte", icon: "microphone" },
{ depth: 1, title: "Dance Tent - Friday", url: "#dance-tent", icon: "location" },
{ depth: 2, title: "DJ SpinCycle (9pm)", url: "#dj-spincycle", icon: "microphone" },
{ depth: 2, title: "GlitterBomb (11pm)", url: "#glitterbomb", icon: "microphone" },
{ depth: 1, title: "Main Stage - Saturday", url: "#main-stage-saturday", icon: "location" },
{ depth: 2, title: "The Acousticats (7pm)", url: "#the-acousticats", icon: "microphone" },
{ depth: 2, title: "Planet Groove (9pm)", url: "#planet-groove", icon: "microphone" },
{ depth: 1, title: "Food & Merch", url: "#food-merch", icon: "store"},
{ depth: 2, title: "Vegan Delights", url: "#food-vegan", icon: "food"},
{ depth: 2, title: "Band T-Shirts", url: "#merch-shirts", icon: "apparel"},
] satisfies CardProps[];
---
<TOC toc={toc} use={Card} />
---
import type { TocItem } from "astro-toc";
export interface CardProps extends TocItem {
icon?: string;
}
type Props = CardProps;
const { title, url, icon, } = Astro.props;
---
<p>
{icon && <i class={`icon ${icon}`} aria-hidden="true"></i>}
{url ? <a href={url}>{title}</a> : <span>{title}</span>}
</p>
<style>
p {
display: flex;
column-gap: 1rem;
}
</style>
API
Component Props (TocProps<T extends TocItem = TocItem>
)
The TOC
component accepts props defined by the generic type TocProps<T extends TocItem = TocItem>
.
- Generic
T
: This represents the shape of a ToC
item within your toc
array. It must include the required depth
and title
fields.
toc | Array<T> | Required | Array of ToC item objects (T extends TocItem ). |
as | bullet | number | menu | bullet | Optional List style: ul (bullet ), ol (number ), or semantic menu |
depth | number | 1 | Optional Minimum heading depth to render (inclusive); controls the starting level. |
maxDepth | number | undefined | Optional Maximum heading depth to render (inclusive) |
use | (props: T) => any | undefined | Optional Custom Astro component to render each ToC item. Receives item (T ) as props. |
HTML Attrs | astroHTML.JSX.HTMLAttributes | n/a | Standard HTML attributes applied to each list container (ul , ol , or menu ), including nested levels. Not applied to li elements. |
TypeScript Types
export interface TocItem {
depth: number;
title: string;
url?: string;
}
export type TocProps<T extends TocItem = TocItem> = {
as?: "bullet" | "number" | "menu";
depth?: number;
maxDepth?: number;
toc: T[];
use?: (item: T) => any;
};
License
Licensed under the ISC License.