Hugo: Add Table of Contents Anywhere in Markdown File
Jun 09, 2020 · 2 Min Read · 28 Likes · 11 CommentsHugo provides a built-in module named {{ .TableOfContents }}
to generate table of contents. It is pretty handy because it generates the table of content without help of any JavaScript or CSS code. But there is a problem with it, you need to put it inside templates. You can’t render it inside the contents of the markdown file. Means you will be able to render it either at top, or at the bottom of your contents.
Usually I prefer to have a small introduction and rest of the contents, have a table of content in between them(like this article).Now we are going to see how to do that using custom shortcodes.
What is shortcodes
Shortcodes is like a tag which can be used inside a markdown file. It will allow you to use built-in or custom templates. You can also pass arguments through shortcode and render them in template. There are some built-in shortcodes like figure
, gist
etc. But we are going to build our own shortcodes.
Create custom shortcodes
First we need to create the shortcodes file named table_of_contents.html
then put it inside layouts/shortcodes
directory. The folder structure will look like this:
Project
├── contents
├── layouts
│ ├── partials
│ └── shortcodes
│ └── table_of_contents.html
├── static
├── themes
└── config.toml
then inside the html file, we need to write the following code:
<div class="toc">
{{ .Page.TableOfContents }}
</div>
Cool. Our shortcode is ready to use.
Usage
We need to use the shortcodes by putting {{< table_of_contents >}}
inside markdown files. For example:
## Your content
Some lines
{{< table_of_contents >}}
More lines
Bonus: add hyperlink to headers
Replace {{ .Page.TableOfContents }}
with the following code:
{{ .Page.TableOfContents | replaceRE "<ul>[[:space:]]*<li>[[:space:]]*<ul>" "<ul>" | replaceRE "</ul>[[:space:]]*</li>[[:space:]]*</ul>" "</ul>" | safeHTML }}
It will generate table of contents with hyperlinks to each header.
In conclusion
Shortcodes are pretty awesome and you can use it in many ways like given here. It allows you to have a cleaner implementation and reusability. But one downside is that, you need to put this shortcode in every markdown file to render the table of contents inside them.
I hope this article helps. If you have any questions, please share in the comment section below.
Last updated: Dec 29, 2024
Thanks - very useful.
You’re welcome 😄.
Helpful, but
replaceRE
won’t work in a shortcode. See: https://github.com/gohugoio/hugo/issues/7095Unless I missed something?
It seems to be working fine for me. I am using Hugo 0.75.1.
Your current implemented ToC look awesome. Can you share it?
Thank you!! 😄 For css, you can check the codes by inspect element at XPath:
//*[@id="blog-post"]/div[3]/div[1]
. Here the actual code of the shortcode:Thanks for share, this is very useful. One little However, the whole thing has a small drawback. Some of the ToC entries are together in one line.
Configuring each tool (H2 heading) vdirsyncer khal khard Todoman <– ((H3 heading) each word is a separate heading and should be in it’s own line) First run of vdirsyncer (H2 heading)
TEST
FYI if replaceRE doesn’t work in your shortcode, you can apply the function to .Content instead. You’ll just need some regex tweaking to target the table of contents in particular, instead of - in the example on this page - every ul or li element.
For example, I decided to get rid of the “TableOfContents” ID and add in an ARIA description instead. For me, this looks like:
Thank you for your feedback. I am using version 0.94.1 and it seems to be working. But I will keep an eye on future releases for this issue.
This is hando to add hugo forms