Neotoc logov1.0.1
Github

Neotoc

Neotoc is a table of contents (TOC) generator library designed to make in-page navigation engaging and useful. On mobile, tap the bottom-right button to open the TOC. On desktop, it appears by default.

It’s built for documentation sites and technical blogs, but you’re free to use it anywhere it feels right.

Want to tinker with Neotoc in your browser? Check out this CodePen.

Features

  • 🌟 Ultra-smooth: It accurately highlights exactly where you are on the page, resulting in an ultra-smooth user experience.
  • 🔄 Auto-scroll: Neotoc keeps itself in sync with your scroll, so you never have to hunt for your position.
  • 📂 Foldable: Easily fold and unfold nested sections.
  • 🎯 Opinionated: Neotoc focuses on a single DOM structure and convention of style, so it stays efficient and free of bloat.
  • 🎨 Ready-made styles: Get styled easily with carefully crafted base styles and color schemes.
  • 🛠️ Framework-agnostic: Neotoc is not tied to any particular library or framework, freeing you to use it anywhere DOM exists.
  • 🚀 Zero dependencies: Built entirely from scratch, with no external dependencies.
  • 🌐 Browser support: Works smoothly across all modern and popular web browsers.
  • Lightweight: The JavaScript bundle is 4.2KB, with ready-made CSS bundle weighing about 2.1KB (both minified and gzipped).
  • Accessible: Accessible with keyboards, touchscreens and screen readers.

Try it out locally

The create-neotoc CLI tool is the easiest way experiment with Neotoc in the comfort of your favorite code editor. It spins up a simple Neotoc playground with configurable dummy content, making it super easy to experiment.

To get started, run the following command in your terminal (you’ll need Node.js installed on your system):

npx create-neotoc

and follow the prompts to set it up.

To learn how to set it up manually read the following section.

Setup guide

Here you will see how to get Neotoc and its ready-made styles. To learn how to use Neotoc to create the TOC UI jump to Neotoc API section.

Neotoc library

Install it from npm with your favorite package manager:

npm install neotoc@1.0.1
pnpm add neotoc@1.0.1
yarn add neotoc@1.0.1
bun add neotoc@1.0.1

You can also use CDNs like jsDelivr to fetch it. For example:

<script
src="https://cdn.jsdelivr.net/npm/neotoc@1.0.1/dist/index.umd.js"
defer
></script>

It is recommended to use the exact version to avoid any breaking changes.

Ready-made styles

Neotoc ships some base styles and color schemes to quickly style it.

Base styles

Base styles style everything other than colors. You must need to add a base style, otherwise the TOC will look like broken.

Available base styles:

  • Modern: Rounded corners with some subtle decorative styles.
  • Plain: No rounded corners and subtle decorative styles.

For example, to load the modern base style from jsDelivr, add the following link tag in the head of your HTML:

<link
href="https://cdn.jsdelivr.net/npm/neotoc@1.0.1/dist/base-modern.css"
rel="stylesheet"
/>

To use any other base style replace modern with the lowercase version of that base style name.

Warning

Make sure you load styles from the same version of neotoc that you are using to avoid any mismatch.

If your project setup allows you to import styles in JS/TS files, you can load it like below:

import "neotoc/base-<name-of-the-base-style>.css";

For example:

import "neotoc/base-modern.css";

Color schemes

Available color schemes:

  • Zinc: Based on TailwindCSS’s zinc color palette.
  • Slate: Based on TailwindCSS’s slate color palette.
  • Monochrome: Black and white.

For example, to load the slate color scheme from jsDelivr, add the following link tag in the head of your HTML:

<link
href="https://cdn.jsdelivr.net/npm/neotoc@1.0.1/dist/colors-slate.css"
rel="stylesheet"
/>

To use any other colorscheme replace slate with lowercase verion of that colorscheme name.

If your project setup allows you to import styles in JS/TS files, you can load it like below:

import "neotoc/colors-<name-of-the-colorscheme>.css";

For example:

import "neotoc/colors-slate.css";

Neotoc API

You can import the TOC UI generator function directly from the package as shown below:

import neotoc from "neotoc";

You can rename neotoc to something else if you wish, as it is the default export.

Info

If you load neotoc as an UMD module from a CDN, then the TOC generator function will be available under the neotoc variable.

neotoc accepts an options object and returns a cleanup function.

If you’re using Neotoc in React or another library/framework, you’ll likely need to call or return this function where it is required. If you are working with a vanilla HTML, CSS, and JS setup, you typically won’t need to use the cleanup function.

Now, let’s explore all of its options in detail.

Options

io and to

Type of io: string
Type of to: HTMLElement

io is the only required option. To create a TOC, you must call neotoc with the io (input-output) option. Here, you need to answer three questions using CSS selectors separated by >>. For example:

neotoc({ io: "article >> h2,h3,h4,h5,h6 >> nav" });
  • Where should it look for headings?
    • Here, it’s the <article> element.
  • Which headings should it consider for creating the TOC?
    • Here, it’s all headings from <h2> to <h6>.
  • Where should it append the generated TOC?
    • Here, it’s the <nav> element.

The last part of the io value can be omitted if you need to provide the corresponding HTML element directly. You can use the to option for this purpose.

Note that if the 3rd part of the io option is specified, to option is ignored.

For Neotoc to work properly, it expects two things

  • The headings you target must have unique IDs.
  • The headings must appear in a logical order. By logical order, I mean:
    • The first heading found determines the highest level. For example, if you start with an <h2> heading, you should not use an <h1> heading later.
    • There should be no gaps between heading levels when progressing from top to bottom. For instance, after an <h2>, you should not use an <h4> without including an <h3> in between.

title

Type: string
Default value: "On this page"

The title of the TOC UI that appears at the top left of it.

fillAnchor

Type: (heading: HTMLHeadingElement) => string | Node
Default value: (heading) => heading.textContent

It is used to determine the content of anchor elements in the TOC. If your headings have a complex structure or contain elements like <code> or <i>, as seen on this page, and you want to display them in the TOC, this option can be useful.

Note

You can’t have the same node in two places in the DOM. You will need to clone it.

ellipsis

Type: boolean
Default value: false

If true, overflowed text is truncated with … instead of wrapping to the next line. When you hover over the anchor, the full text is displayed in the browser’s default tooltip.

Tip

When using fillAnchor with cloned heading nodes containing <code> elements, set their position to static in the TOC if it’s not, to ensure the ellipsis appears correctly.

classPrefix

Type: string
Default value: nt-

All CSS classes assigned to elements in the TOC UI are prefixed with nt- by default to avoid class name collisions.

If the nt- prefix is already used in class names elsewhere on your page, you should set a different value here.

Warning

If you set anything else here, as a side-effect, you can’t directly use styles that come with neotoc. You need to copy these styles and replace all occurrences of the default nt- prefix with your new class prefix and then use them.

initialFoldLevel

Type: number
Default value: 6

It should be a number from 1 to 6.

If it is 1, <h1> and all lower-level headings’ corresponding sections in the TOC will be folded initially.

If it is 2, <h2> and all lower-level headings’ corresponding sections in the TOC will be folded initially.

And so on.

Note that if it is 6, everything is unfolded initially since there is no <h7>.

offsetTop and offsetBottom

Type of both: number
Unit: px
Default value of both: 0

Neotoc considers the portion of the nearest scroll container(usually the <html> element) cropped by the screen as its viewport.

This viewport is reflected in the TOC to highlight the relevant area.

You can lower the top edge of this viewport using the offsetTop option and raise the bottom edge of the viewport using offsetBottom.

This is useful if you have a fixed header or footer.

Note: Nearest scroll container

Here “nearest scroll container” refers to the nearest scroll container of the element matched by the first part of the io option.

Tip: If you have fixed header...

If you have a fixed header, to ensure the headings scroll to the correct position, you can use the CSS scroll-margin-top property on the heading elements.

autoScrollOffset

Type: number
Unit: px
Default value: 50

This option allows you to control the distance the highlighted area should stay from the top and bottom edges of the main TOC UI (the element that by default has the class .nt-body) when possible.

Customizing color schemes

Changing colors of different parts of the TOC UI is as simple as assigning some color value to the following CSS variables, which are defined on the root TOC UI element (with the default class nt-widget):

  • --bg
  • --fg
  • --bg-anchor-hover
  • --bg-anchor-active
  • --bg-sub-anchors
  • --bg-header-btn
  • --bg-header-btn-hover
  • --bg-header-btn-active
  • --bg-header-btn-disabled
  • --fg-header-btn-disabled
  • --border-header-btn
  • --bg-toggle-fold-btn-hover
  • --bg-toggle-fold-btn-active
  • --indent-line
  • --indent-line-highlight
  • --fold-indicator-gradient-mid
  • --light-bar
  • --light-bar-tip-on-fold
  • --light-opacity

You can change colors responsively using media queries. Additionally, you can define different colors for light and dark modes using CSS selectors.

Tip

If you want to interactively change these variables and see the result on the page, you can use your browser’s developer tools.

For example if you only want the light bar color to orange while keeping rest of your color scheme the same, add the following in a stylesheet that comes after the color scheme stylesheet:

.nt-widget {
--light-bar: orange;
}

If you are not statisfied with changing a color or two, you can even create your own color schemes from scratch in the same way and use it instead of modifying an existing color scheme.

Customizing the base style

You can modify various aspects of the base style using the following CSS variables:

  • --body-height
  • --relative-font-size
  • --toggle-fold-btn-width
  • --indent-line-gap
  • --indent-line-width
  • --anchor-padding-block
  • --anchor-padding-inline
  • --anchor-border-radius
  • --padding-left
  • --light-bar-width
  • --light-bar-tip-radius
  • --light-spread-length
  • --folding-duration

Info

Of these few variables should be in some specific units or types:

  • --relative-font-size: It should not have any units. Just plain number.
  • --toggle-fold-btn-width: It should be in em.
  • --indent-line-gap: It should be in px.
  • --folding-duration: It should be of <time> type.

The rest of the variables can be of any <length> type.

Of these, --body-height is the one you will most likely want to modify. It describes the height of body of the TOC UI that is the area except the header of the TOC UI. The default value of this is 400px. Suppose you want this height to be something dynamic like calc(100vh - 5rem). Then add the following in a stylesheet that comes after the base stylesheet of your choice:

.nt-widget {
--body-height: calc(100vh - 5rem);
}

Tip

If you want to interactively change any of these variables and see the result on the page, you can use your browser’s developer tools.

Found a typo? Other suggestions?
Edit this page on Github

Last modified: 2025-05-25