How to Add Tocbot (Table of Contents) to your Hugo Site

I’ve found Tocbot to be the cleanest table of contents (TOC) generator. It highlights and bolds as you scroll, uses vanilla JavaScript (avoids dependencies), works on all modern browsers, and is incredibly configurable.

If you want to see it in action, check out the Tocbot website.

On many of the projects I’ve worked on, I’ve found myself using Hugo along with Tocbot, so I want to demonstrate how I get this up and running.

Download Tocbot

Let’s download the latest release of Tocbot from Github.

We’ll need to unzip the folder to access the files.

We only really need to focus on the /dist folder in the root directory.

Inside /dist, there are three files we want:

  • styles.css
  • tocbot.css
  • tocbot.min.js

Let’s copy these guys and head over to our Hugo project.

Add Tocbot Files to Our Hugo Project

In your file system, navigate to where all your css and js assets are.

They’ll most likely be in your /static or /assets directory, which can be configured in your config.toml through the staticDir and assetDir keys.

Paste the single .js file and the two .css files into their respective folders.

Find the .html file that contains the <head> tag (it’s very likely head.html).

We’ll want to link to these .css files in our <head> tag.

<!-- Tocbot Styles -->
<link rel="stylesheet" href="[css_dir_path]/tocbot.css">
<link rel="stylesheet" href="[css_dir_path]/styles.css">

One random caveat: Tocbot will not properly track scroll height (and therefore will not highlight the active heading) if overflow-x: hidden is set for the page’s body tag.

Specify Location of TOC

I placed my TOC in my sidebar.html.

Unfortunately, this may not be available to you depending on your theme and layout.

Many people do place it in their single.html file.

<!--Tocbot TOC Placeholder -->
<div class="js-toc"></div>

Your class name does not need to be js-toc.

Specify Location of Content

Your Tocbot will dynamically generate your TOC by searching for header tags (<h1>, <h2>, etc.) in your content.

But it doesn’t know where the content is, so that’s why we need our content to be wrapped inside a <div> with class js-toc-content.

This might mean you’re creating a new <div> around your content or simply adding a new class to an already existing <div> (this latter option makes more sense to me).

Search all your .html files for .Content, since this is where Hugo will generate the posts from your content folder.

It is likely inside single.html, which is where I put this code snippet.

<!-- Post Content -->
<div class="article-post js-toc-content">
    {{ .Content }}

Again, the class name does not need to be js-toc-content.

Initialize Tocbot Scripts

The first thing we want to do is add the tocbot.min.js file we downloaded earlier to our Hugo project.

We generally have two options:

  1. Add Tocbot to every page on our site: add <script> tag to baseof.html
  2. Add Tocbot only to article pages: add <script> tag to single.html
<script src="[js_dir_path]/tocbot.min.js"></script>

Next, we want to initialize Tocbot directly after the <script> tag above.

We can use tocbot.init() to do this. We need to specify where to render the TOC and where the content is.

<!-- tocbot options -->
<script type="text/javascript">
    // Where to render the table of contents.
    tocSelector: '.js-toc',
    // Where to grab the headings to build the table of contents.
    contentSelector: '.js-toc-content',
    // Which headings to grab inside of the contentSelector element.
    headingSelector: 'h1, h2, h3'

If you changed either class name (.js-toc or .js-toc-content) in a section above, make sure you reflect that change in the initialization here.

For more ways to customize your Tocbot, be sure to check out the official Tocbot website.


There you have it! You should have a working TOC for your Hugo site.

This is a fairly straightforward and simple process, but it can be confusing when you’re first starting out with Hugo.

Good luck starting your site!