When using content management system like WordPress, adding table of contents to your posts is quite straightforward – just install a plugin, and that’s it.

However, when using a static site generator like Hugo, that too when you use markdown to write content, it may not be that obvious.

So in this quick post, we will discuss how to set up table of contents in your Hugo blog.

We’ll be discussing two methods below:

  • add table of contents within posts content using shortcodes
  • add it in the template

The toc shown above is generated with the first method

Watch Video

By playing this video, you agree to YouTube's Terms
Watch on YouTube →

In the first method that uses shortcode, you will have to call the shortcode tag manually in each markdown file. The advantage is, you can place it anywhere in the post – before the first heading, after the first paragraph, etc.

In the second method, we will add the table of contents in our single.html template file. So the table content will be added automatically to all our post pages when Hugo renders the HTML. However, the downside is, you can only add it at the beginning or end of a post.

Before that, you should know about two things:

  • Hugo’s .TableOfContents variable, and
  • autoHeadingIDs in Goldmark renderer

The .TableOfContents Variable

Hugo offers a built-in page variable called .TableOfContents, which contains the markup for table of contents.

So you don’t need to parse anything manually. Just like you pull front matter data using Hugo page variables, you can call this variable inside your templates.

Ensuring autoHeadingID is enabled

In addition to the .TableOfContents variable, this is another handy feature that Hugo offers by default.

If you are not aware, Hugo makes use of the Goldmark markdown parser to convert your content files to HTML. And during that process, it also generates unique ID attributes for all headings based on its text.

Although the autoHeadingID is enabled by default, some themes or configurations may disable it from the site’s config file. If it is explicitly disabled, the anchor links in the table of contents won’t work.

So, ensure that your theme or site’s config file does not set the autoHeadingID value to false.

Method 1: Adding Table Of Contents in Markdown

Create a Shortcode

Create a file called toc.html with the below HTML markup, and place it inside the layouts/shortcodes directory in your site’s root. If you are using a theme, you can place it there also.

    <h2>Table Of Contents</h2>
    {{ .Page.TableOfContents }}

By the way, the name of the file doesn’t matter, although here we named it as toc.html.

Also, note that we have to add .Page variable in front of the .TableOfContents variable, since we are calling it inside a shortcode. If it were in a template file, we could directly call it without prefixing .Page.

Call the Shortcode inside Markdown

Now you can call the above shortcode from any markdown file in your content directory:

{{</* toc */>}}
calling shortcode inside markdown

Note that the name of the shortcode is the same as its file name without the .html part.

Method 2: Adding TOC in Template

Adding table of contents in the template is even more simple. You can just call the .TableOfContents variable directly inside your single.html file like this:

Adding TOC in template file


The built-in variable supports three configuration variables:

    endLevel: 3
    ordered: false
    startLevel: 2

The endLevel and startLevel specifies the heading levels that you want to include in the TOC. With the above code, it includes h2 and h3 tags.

Instead, if you want to limit it to h2 tags only, set both the endLevel and startLevel values to 2.

With the ordered value set to false, which is the default value, your table of contents will show up in an HTML unordered list <ul>. Instead if you set it to true, it will be rendered inside an ordered list <ol>.