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

@todovue/tv-toc

v1.1.0

Published

A Vue 3 component to generate a table of contents (TOC) for your articles or documentation, enhancing navigation and user experience.

Readme

TODOvue TOC Component (TvToc)

A lightweight Vue 3 component to render a Table of Contents (TOC) for your articles or documentation, with smooth scrolling and nested sections support.

npm npm downloads npm total downloads License Release Date Bundle Size Node Version Last Commit Stars

Demo: https://ui.todovue.blog/toc

Table of Contents

Features

  • Simple and focused Table of Contents (TOC) component for Vue 3.
  • Supports nested sections via children links.
  • Smooth scrolling to headings using scrollIntoView.
  • URL hash is updated using history.pushState for better navigation and shareable links.
  • Works in SPA (Vite, Vue CLI) and Nuxt 3 (with client-side rendering constraints).
  • Ships with minimal, customizable styles.

Installation

Using npm:

npm install @todovue/tv-toc

Using yarn:

yarn add @todovue/tv-toc

Using pnpm:

pnpm add @todovue/tv-toc

Usage of Styles

Vue/Vite (SPA)

Import the CSS generated by the library in your main entry file:

// main.ts
import { createApp } from 'vue'
import App from './App.vue'

import '@todovue/tv-toc/style.css'
import { TvToc } from '@todovue/tv-toc'

const app = createApp(App)
app.component('TvToc', TvToc)
app.mount('#app')

Nuxt 3/4

Add the library's CSS to your Nuxt configuration:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@todovue/tv-toc/nuxt'
  ]
})

Quick Start (SPA)

Global registration (main.js / main.ts):

import { createApp } from 'vue'
import App from './App.vue'
import TvToc from '@todovue/tv-toc'

createApp(App)
  .component('TvToc', TvToc)
  .mount('#app')

Local import inside a component:

<script setup>
import { TvToc } from '@todovue/tv-toc'

const toc = {
  title: 'On this page',
  links: [
    { id: 'introduction', text: 'Introduction' },
    {
      id: 'getting-started',
      text: 'Getting started',
      children: [
        { id: 'installation', text: 'Installation' },
        { id: 'basic-usage', text: 'Basic usage' },
      ],
    },
    { id: 'api', text: 'API Reference' },
  ],
}
</script>

<template>
  <div class="page-layout">
    <main class="page-content">
      <h2 id="introduction">Introduction</h2>
      <!-- ... -->
      <h2 id="getting-started">Getting started</h2>
      <h3 id="installation">Installation</h3>
      <h3 id="basic-usage">Basic usage</h3>
      <!-- ... -->
      <h2 id="api">API Reference</h2>
    </main>

    <aside class="page-toc">
      <TvToc :toc="toc" />
    </aside>
  </div>
</template>

Nuxt 4 / SSR Usage

Create a plugin file: plugins/tv-toc.client.ts (client-only because it uses document and history under the hood when scrolling):

import { defineNuxtPlugin } from '#app'
import TvToc from '@todovue/tv-toc'

export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.vueApp.component('TvToc', TvToc)
})

Use anywhere (recommended inside <client-only> when using Nuxt 3):

<template>
  <client-only>
    <TvToc :toc="toc" />
  </client-only>
</template>

Direct import (no plugin):

<script setup>
import { TvToc } from '@todovue/tv-toc'
</script>

<template>
  <TvToc :toc="toc" />
</template>

Component Registration Options

| Approach | When to use | |-----------------------------------------------|-----------------------------------| | Global via app.component('TvToc', TvToc) | Frequent use across the app | | Local named import { TvToc } | Isolated/code-split contexts | | Direct default import import TvToc from ... | Single use or manual registration |

Props

| Name | Type | Default | Description | Required | |-----------------|---------|------------|----------------------------------------------------------------------------|----------| | toc | Object | - | TOC configuration: title and list of links (with optional nested children) | true | | marker | Boolean | false | Whether to display a visual marker for the active item. | false | | collapsible | Boolean | false | Whether sublists can be collapsed/expanded. | false | | activeClass | String | 'active' | Custom CSS class for the active item. | false | | observerOptions | Object | {} | options to pass to the IntersectionObserver (rootMargin, threshold, etc). | false |

toc shape

type TocLink = {
  id: string
  text: string
  children?: TocLink[]
}

type Toc = {
  title?: string
  links: TocLink[]
}
  • title: Optional title shown at the top of the TOC (h3).
  • links: Array of top-level sections.
    • id: Must match the id attribute of the target heading in your content.
    • text: Label shown in the TOC.
    • children: Optional array of subsections, rendered as nested list.

Composable: useToc

This composable is used internally by TvToc but can also be imported directly if needed.

import { useToc } from '@todovue/tv-toc'

const { formatId, scrollToId } = useToc(links, {
  rootMargin: '0px 0px -50% 0px'
})

API

| Name | Type | Description | |------------|--------------------------|----------------------------------------------------------------| | formatId | (id: string) => string | Returns a hash-based id string (e.g. "#section-id"). | | scrollToId | (id: string) => void | Smooth scrolls to the element with that id and updates hash. |

Note: scrollToId accesses document and history, so it should run only in the browser (e.g. in event handlers or inside onMounted).

Customization (Styles)

The component ships with minimal default styles, exposed through the built CSS file and scoped CSS classes.

Main CSS classes:

  • .tv-toc — Root <nav> container.
  • .tv-toc-title — Title of the TOC.
  • .tv-toc-list — Top-level list container.
  • .tv-toc-item — Top-level list item.
  • .tv-toc-link — Anchor for top-level items.
  • .tv-toc-sublist — Nested list container for children.
  • .tv-toc-subitem — Nested list item.
  • .tv-toc-sublink — Anchor for nested items.

You can override these styles in your own global stylesheet:

/* example overrides */
.tv-toc {
  font-size: 0.9rem;
}

.tv-toc-link,
.tv-toc-sublink {
  color: #4b5563;
}

.tv-toc-link:hover,
.tv-toc-sublink:hover {
  color: #111827;
}

If you are using SCSS, you can also rely on your own design tokens and overrides. The package itself internally uses SCSS (see src/assets/scss/_variables.scss and src/assets/scss/style.scss).

SSR Notes

  • The component can be rendered on the server (template is static), but scrolling behavior uses browser APIs.
  • scrollToId uses document.getElementById and history.pushState; these are only invoked in event handlers on the client.
  • When using Nuxt 3, prefer registering TvToc in a *.client.ts plugin or wrap usages in <client-only> to avoid hydration edge cases in environments with stricter SSR.

Examples

This repository includes a small demo application built with Vite.

  • Basic TOC example.
  • Blog-like layout with nested headings.

To run the demo locally, see the Development section.

Development

git clone https://github.com/TODOvue/tv-toc.git
cd tv-toc
npm install
npm run dev     # run local demo
npm run build   # build library

The local demo is served with Vite using index.html and examples in src/demo.

To build the standalone demo used for documentation:

npm run build:demo

Contributing

PRs and issues are welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

License

MIT © TODOvue

Attributions

Crafted for the TODOvue component ecosystem