Location via proxy:   
[Report a bug]   [Manage cookies]                
v0.2.3
Github

Neotoc—Table of Contents Reimagined

By Ashutosh Biswas

Welcome! On mobile, tap the bottom-right button to open the TOC. On desktop, you’ll see it right away. Scroll around, fold/unfold, and give it a try if you haven’t already—pretty cool, right?

Let’s get started.

The playground

Check out this CodePen for experimenting with it instantly.

To easily experiment with it in the comfort of your favorite code editor and with some extra tooling, I’ve created a CLI called create-neotoc. It scaffolds a Vite-powered Neotoc playground. 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. It will look something like below:

create neotoc cli

Doing npm run dev will start a local web server. Open the given URL in a web browser and you will find further instructions there.

The playground is just for experimenting and learning. But if you’d like to start from scratch, feel free to go for it!

Installation

From npm:

npm install neotoc@0.2.3

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

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

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

Adding 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@0.2.3/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 shemes:

  • 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@0.2.3/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";

Requirements

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.

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.

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.

onBreadcrumbChange

Type: (data: Breadcrumb) => void
Default value: () => {}

Here Breadcrumb type is:

type Breadcrumb = {
content: string | Node;
hash: string;
}[]

You can also import the type from Neotoc if needed.

Neotoc provides the data at the right time but leaves the breadcrumb construction in your hands.

The value of content depends on what is returned from the fillAnchor option. You will need the hash if you want to create links for the breadcrumb items to jump to the parent section.

Note: Cleaning up side effects

Since Neotoc leaves breadcrumb construction to you, you will also need to clean up any side effects, if required. This is typically necessary when using it with a frontend library or framework.

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.

autoFold

Type: boolean
Default value: false

Whether or not to automatically fold/unfold the TOC based on what is in the viewport.

autoScroll

Type: boolean
Default value: true

Whether or not to automatically scroll the TOC to keep the highlighted portion in view when the nearest scroll container is scrolled.

autoScrollOffset

Type: number
Unit: px
Default value: 50

When autoScroll is true, 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.

toggleFoldIcon

Type: string
Default value: Chevron down icon(SVG)

This icon is shown at the left of each anchor text on the TOC which has sub anchors underneath it to allow you to fold/unfold it. You can set another SVG icon here with this option.

unfoldableIcon

Type: string
Default value: Dot icon(SVG)

This icon is shown at the left of each anchor text on the TOC which do not have sub anchors underneath it. You can set another SVG icon here with this option.

foldIcon

Type: string
Default value: Unfold less rounded icon(SVG)

This is used on the fold button on the header of the TOC. You can set another SVG icon here with this option.

unfoldIcon

Type: string
Default value: Unfold more rounded icon(SVG)

This is used on the unfold button on the header of the TOC. You can set another SVG icon here with this option.

foldAllIcon

Type: string
Default value: Unfold less double rounded icon(SVG)

This is used on the fold all button on the header of the TOC. You can set another SVG icon here with this option.

unfoldAllIcon

Type: string
Default value: Unfold more double rounded icon (SVG)

This is used on the unfold all button on the header of the TOC. You can set another SVG icon here with this option.

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-03-24