npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

astro-toc-generator

v1.1.2

Published

Table of Contents generator for Astro.js with Sanity integration

Readme

Alt Text

⚡Astro TOC Generator for Sanity

npm version npm downloads TypeScript Built with Astro

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

  1. 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>
  1. 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

  1. Heading IDs: Add IDs to your headings using the generateSlug function
  2. CSS Variables: Customize appearance using CSS variables in :root
  3. Scroll Behavior: Add scroll-behavior: smooth to your root element
  4. Scroll Margins: Add scroll-margin-top to 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

  1. 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;
}
  1. 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;
}
  1. Change active state colors:
:root {
  --toc-active-color: red;
  --toc-active-bg: rgba(255, 0, 0, 0.05);
}
  1. Modify indentation:
:root {
  --toc-indent-size: 1.5rem;
}
  1. Change container appearance:
:root {
  --toc-bg: #fff;
  --toc-border-radius: 12px;
}

.toc-container {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
  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

Open in StackBlitz Open with CodeSandbox Open in GitHub Codespaces

💼 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