astro-toc-generator
v1.1.2
Published
Table of Contents generator for Astro.js with Sanity integration
Maintainers
Readme

⚡Astro TOC Generator for Sanity
A table of contents generator for Astro.js that works specifically with Sanity CMS blog content. This component automatically generates a table of contents from your Sanity portable text content, with support for custom styling and smooth scrolling.
💡 Features
- 🎯 Specifically designed for Sanity CMS content
- 🎨 Fully customizable styling with CSS variables
- 🔄 Active section highlighting with parent-child relationships
- 🔗 Smooth scrolling support
- 📚 Support for all heading levels (h1-h6)
- 🌳 Hierarchical structure support
- 🎨 Different styling per heading level
- 🎯 Accurate scroll position tracking
📥 Installation
npm install astro-toc-generator💻 Usage
- Import the component and styles in your Astro page:
---
import { TableOfContents, generateSlug } from 'astro-toc-generator';
import 'astro-toc-generator/styles'; // Import base styles
// Your Sanity portable text content
const content = [
{
_type: 'block',
style: 'h1',
children: [{ _type: 'span', text: 'Welcome to My Blog' }]
},
// ... more content blocks
];
---
<div class="layout">
<aside>
<TableOfContents
content={body?.value}
maxLevel={3}
containerClass="toc-container"
smoothScroll={true}
activeClass="active"
linkClass="toc-link"
listClass="toc-list"
listItemClass="toc-item"
headingSelector="h1, h2, h3"
/>
</aside>
<article>
<!-- Important: Add matching IDs to your headings using generateSlug -->
<h1 id={generateSlug('Welcome to My Blog')}>Welcome to My Blog</h1>
<h2 id={generateSlug('Introduction')}>Introduction</h2>
<!-- ... more content -->
</article>
</div>- Add required CSS and customize the styling:
:root {
/* Basic setup */
scroll-behavior: smooth;
/* Customize TOC appearance */
--toc-bg: #f8f9fa;
--toc-text-color: #666;
--toc-border-color: #eaeaea;
--toc-active-color: #0d6efd;
--toc-active-bg: rgba(13, 110, 253, 0.05);
--toc-hover-color: #000;
--toc-hover-bg: rgba(0, 0, 0, 0.05);
--toc-indent-size: 1rem;
--toc-border-radius: 8px;
--toc-transition: all 0.2s ease;
}
/* Layout setup */
.layout {
display: grid;
grid-template-columns: 3fr 1fr;
gap: 2rem;
}
aside {
position: sticky;
top: 0;
align-self: start;
height: 100vh;
overflow-y: auto;
padding-top: 2rem;
}
/* Add scroll margin to headings */
h1,
h2,
h3,
h4,
h5,
h6 {
scroll-margin-top: 2rem;
padding-top: 2rem;
}
/* Optional: Add box shadow to TOC */
.toc-container {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Optional: Override specific styles */
.toc-link.active {
color: red; /* Change active color */
}📌 Important Notes
- Heading IDs: Add IDs to your headings using the
generateSlugfunction - CSS Variables: Customize appearance using CSS variables in
:root - Scroll Behavior: Add
scroll-behavior: smoothto your root element - Scroll Margins: Add
scroll-margin-topto headings for proper positioning
📝 Supported Props
| Prop | Type | Default | Description | | --------------- | ---------------------------- | ------------------------ | -------------------------------------- | | content | PortableTextBlock[] or any[] | required | Your Sanity portable text content | | containerClass | string | 'toc-container' | Class for the container element | | listClass | string | 'toc-list' | Class for the list element | | listItemClass | string | 'toc-item' | Class for list items | | linkClass | string | 'toc-link' | Class for links | | activeClass | string | 'active' | Class for active links | | smoothScroll | boolean | true | Enable/disable smooth scrolling | | maxLevel | number | 6 | Maximum heading level to include (1-6) | | headingSelector | string | 'h1, h2, h3, h4, h5, h6' | Selector for headings to track |
🚀 Heading Level Mapping
The component automatically maps Sanity heading styles to their corresponding HTML levels:
| Sanity Style | HTML Level | Description | | ------------ | ---------- | -------------------------- | | h1 | Level 1 | Main title/chapter heading | | h2 | Level 2 | Section heading | | h3 | Level 3 | Subsection heading | | h4 | Level 4 | Sub-subsection heading | | h5 | Level 5 | Minor heading | | h6 | Level 6 | Smallest heading |
Level-based CSS Classes
Each TOC item receives classes based on its heading level for targeted styling:
/* Target specific heading levels */
.toc-item.level-1 {
/* h1 headings */
}
.toc-item.level-2 {
/* h2 headings */
}
.toc-item.level-3 {
/* h3 headings */
}
.toc-item.level-4 {
/* h4 headings */
}
.toc-item.level-5 {
/* h5 headings */
}
.toc-item.level-6 {
/* h6 headings */
}
/* Example: Style different levels differently */
.toc-item.level-1 .toc-link {
font-weight: bold;
font-size: 1.1em;
}
.toc-item.level-2 .toc-link {
font-weight: 600;
}
.toc-item.level-3 .toc-link {
font-weight: normal;
opacity: 0.8;
}Depth-based Classes
The component also provides depth classes for nested items:
/* Target by nesting depth */
.toc-item.depth-0 {
/* Root level items */
}
.toc-item.depth-1 {
/* First level nested items */
}
.toc-item.depth-2 {
/* Second level nested items */
}
/* Example: Increase indentation per depth level */
.toc-item.depth-1 {
padding-left: calc(var(--toc-indent-size) * 1);
}
.toc-item.depth-2 {
padding-left: calc(var(--toc-indent-size) * 2);
}🪄 CSS Variables
| Variable | Default | Description | | ------------------- | ------------------------ | ---------------------------------- | | --toc-bg | #f8f9fa | Background color of the TOC | | --toc-text-color | #666 | Default text color | | --toc-border-color | #eaeaea | Border color | | --toc-active-color | #0d6efd | Color for active items | | --toc-active-bg | rgba(13, 110, 253, 0.05) | Background for active items | | --toc-hover-color | #000 | Text color on hover | | --toc-hover-bg | rgba(0, 0, 0, 0.05) | Background on hover | | --toc-indent-size | 1rem | Indentation size for nested items | | --toc-border-radius | 8px | Border radius of the container | | --toc-transition | all 0.2s ease | Transition for hover/active states |
🛠️ Utility Functions
import { generateSlug } from "astro-toc-generator";
// Generate URL-friendly slugs for heading IDs
const slug = generateSlug("Your Heading Text");✏️ Customization Examples
- Style different heading levels:
/* Main headings (h1) - larger and bold */
.toc-item.level-1 .toc-link {
font-size: 1.2em;
font-weight: bold;
color: #000;
}
/* Subheadings (h2) - medium weight */
.toc-item.level-2 .toc-link {
font-size: 1.1em;
font-weight: 600;
color: #333;
}
/* Minor headings (h3+) - lighter */
.toc-item.level-3 .toc-link,
.toc-item.level-4 .toc-link,
.toc-item.level-5 .toc-link,
.toc-item.level-6 .toc-link {
font-size: 1em;
font-weight: normal;
color: #666;
}- Progressive indentation by level:
.toc-item.level-2 {
padding-left: 1rem;
}
.toc-item.level-3 {
padding-left: 2rem;
}
.toc-item.level-4 {
padding-left: 3rem;
}
.toc-item.level-5 {
padding-left: 4rem;
}
.toc-item.level-6 {
padding-left: 5rem;
}- Change active state colors:
:root {
--toc-active-color: red;
--toc-active-bg: rgba(255, 0, 0, 0.05);
}- Modify indentation:
:root {
--toc-indent-size: 1.5rem;
}- Change container appearance:
:root {
--toc-bg: #fff;
--toc-border-radius: 12px;
}
.toc-container {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}- Custom hover effects:
:root {
--toc-hover-color: #0d6efd;
--toc-hover-bg: rgba(13, 110, 253, 0.1);
}📚 Example Content Structure
Here's how your Sanity content structure should look:
const content = [
// Level 1 - h1
{
_type: "block",
style: "h1",
children: [{ _type: "span", text: "Main Title" }],
},
// Level 2 - h2
{
_type: "block",
style: "h2",
children: [{ _type: "span", text: "Section Title" }],
},
// Level 3 - h3
{
_type: "block",
style: "h3",
children: [{ _type: "span", text: "Subsection Title" }],
},
];📦 Quick Start & Development
npm create astro@latest -- --template minimal💼 Explore my personal work and projects:
Visit my portfolio at Bhargav Patel to explore my work, projects, and what I’ve been building lately.
🪪 License
MIT
