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 🙏

© 2026 – Pkg Stats / Ryan Hefner

sanity-plugin-table-of-contents

v2.0.0

Published

Add a table of contents to your documents for page building or long-form content in Portable Text (PT). You can configure which array or PT fields should be taken into account. The plugin will then generate a list with links from each entry to the corresp

Readme

Table of Contents for your Sanity Studio

This is a plugin that adds a table of contents inspector to navigate your page builder arrays and Portable Text in documents, letting you quickly jump to a specific heading or custom block - even for nested Portable Text fields.

Features

  • Table of contents inspector: Navigate your Portable Text and array fields and quickly jump to a specific heading or block.
  • Click to jump: Selecting an entry focuses and opens the matching block or array item in the document editor.
  • Nested & embedded fields: Resolves headings inside nested Portable Text and array items — including Portable Text / array fields embedded within page-builder blocks.
  • Customisable: Choose which document types and which array / Portable Text fields to include.
  • Translatable: Ships an English resource bundle and integrates with Studio i18n so you can override or add locales.
  • Lightweight: Minimal impact on your Studio's performance.

Compatibility

2.x targets Sanity Studio 6 and is the latest release; 1.x stays on Sanity 5 as a maintenance line.

| Plugin version | Sanity Studio | React | Node | | -------------- | -------------- | ----- | ------- | | ≥ 2.0.0 | 6.x | 19 | ≥ 22.12 | | 1.0.x | 5.x (≥ 5.11.0) | 19 | ≥ 18 |

Why the split? Sanity Studio 6 requires React 19 and Node ≥ 22.12. A Studio major means a plugin major, so pin the line that matches your Studio:

# Sanity Studio 6
npm install sanity-plugin-table-of-contents        # latest (≥ 2.0.0)
# Sanity Studio 5
npm install sanity-plugin-table-of-contents@^1

Migrating from 1.x

2.0.0 is a platform bump, not an API change. The plugin configuration (fieldNames, documentTypes) is unchanged — there is nothing to rewrite in your sanity.config.

2.x targets Sanity Studio 6 (React 19, Node ≥ 22.12). If you're still on Sanity 5, stay on the ^1 line (see Compatibility). Otherwise bump Sanity, React and Node together, then install sanity-plugin-table-of-contents@^2.

Installation

npm install sanity-plugin-table-of-contents
# or
pnpm add sanity-plugin-table-of-contents
# or
yarn add sanity-plugin-table-of-contents

Usage

Add it as a plugin in sanity.config.ts (or .js):

import { defineConfig } from 'sanity'
import { tableOfContentsPlugin } from 'sanity-plugin-table-of-contents'

export default defineConfig({
  //...
  plugins: [
    //...
    tableOfContentsPlugin({
      fieldNames: ['content', 'body'], // array and Portable Text fields to include in the TOC
      documentTypes: ['page', 'news'], // document types that get the TOC inspector
    }),
  ],
})

Open a document of one of the configured documentTypes and choose Table of Contents from the inspector menu (the icons in the top-right of the document pane). The inspector lists the entries found in your configured fields; click one to jump to it in the editor.

Configuration options

| Option | Type | Required | Description | | --------------- | ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | fieldNames | string[] | yes | Names of the array and Portable Text fields to build the table of contents from. Fields with these names that are embedded inside blocks are resolved too. | | documentTypes | string[] | yes | Document types the inspector is added to. Documents of other types are left untouched. |

Note: fieldNames matches by field name, so a page-builder array and a Portable Text field can share the plugin, and the same field name nested inside a block (e.g. a Portable Text field inside a page-builder section) is picked up automatically.

How it works

  • The plugin registers a document inspector for the configured documentTypes and contributes its label through the Studio i18n system.
  • The inspector reads the live document form state, so entries stay in sync as you edit.
  • Each entry resolves a readable title from the block or array item (Portable Text heading text, or an item's title/first text), indented to reflect nesting.
  • Clicking an entry focuses and opens the corresponding path in the editor, expanding nested inputs as needed.

TypeScript support

The plugin is written in TypeScript and exports the config type for consumers:

import type { TableOfContentsPluginOptions } from 'sanity-plugin-table-of-contents'

const tocConfig: TableOfContentsPluginOptions = {
  fieldNames: ['body'],
  documentTypes: ['page'],
}

Internationalisation

The plugin ships an English resource bundle under the table-of-contents namespace and registers it through the Studio i18n bundles API. To translate the UI (inspector title, close button labels) into another locale, add your own resource bundle for the table-of-contents namespace in your Studio's i18n configuration.

License

MIT © Saskia Bobinska

Develop & test

This plugin uses @sanity/plugin-kit and @sanity/pkg-utils for build & watch scripts.

See Testing a plugin in Sanity Studio on how to run this plugin with hotreload in the Studio. This repo also includes a testing-studio workspace — run it against the linked plugin with:

pnpm dev

Package manager

The repo pins its pnpm version through the packageManager field in package.json, so local development and CI run the exact same pnpm. Enable Corepack once and your pnpm will match automatically:

corepack enable

Node ships Corepack; if pnpm doesn't pick up the pinned version, run corepack prepare --activate. Bump the version in the packageManager field to upgrade — nothing else needs changing.

Release new version

Releases are automated with semantic-release and published to npm via OIDC trusted publishing (no npm token needed). Merging Conventional Commits into main triggers the release: fix: → patch, feat: → minor, feat!: / BREAKING CHANGE: → major. Commit messages are linted locally via a Husky commit-msg hook.

You can also run the "CI & Release" workflow manually from GitHub Actions and check "Release new version". Semantic release only publishes on the configured branches (main, 1.x), so it is safe to run on any branch.

Kudos

Thanks to @jordanl17 for his help getting this over the line!