Developer docs

How to add a table of contents to your Ghost site

Long-form content sometimes require extra UI to help the viewer navigate the content quickly. A table of contents is a list of links shown above or beside the main content that once clicked will take the viewer to a headed section within the page. In this tutorial we’ll show you how to automatically generate a table of contents from the section headings in your content.

Example of a table of contents beside a post

Prerequisites

This tutorial shows how to add a table of contents to your posts and pages via Ghost admin, however the full implementation does require downloading and editing your Ghost theme. To learn more check out our extensive Handlebars theme documentation.

Adding the library

For this tutorial we’ll be making use of a small library called ‘Tocbot’, which generates a table of contents from headings in your content. To add Tocbot to your Ghost site navigate to the Code Injection view in Ghost admin and use the cdnjs.com directory to copy the main prism.js script tag and style link tag.

Tocbot CSS tag on cdnjs.com

Tocbot JS tag on cdnjs.com

You can either copy the tags directly from cdnjs.com or copy the code below. As of writing this tutorial Tocbot is at version 4.10.0, please check you’re using the latest version.

<!-- link tag -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.10.0/tocbot.css" />

<!-- script tag -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.10.0/tocbot.min.js"></script>

Paste the style link tag into the Site Header section, and the script tag into the Site Footer section:

Tocbot being added to the two code injection areas within Ghost admin

Click save to ensure the tags have been added to your site.

Displaying a table of contents

The table of contents library has been added but it needs to be activated. Add the following code below the Tocbot script tag within the Site Footer code injection area:

<script>
    tocbot.init({
        tocSelector: '.toc',
        contentSelector: '.post-content',
        hasInnerContainers: true
    });
</script>

Note the part .post-content, this is selector that is on the element that immediately wraps the post and page content on your Ghost site. In Casper it’s .post-content. You can find this out by viewing the HTML structure, either by using “View Source” or “Inspect” in a web browser, and locating the element which immediately wraps the main content. Below is an example of the HTML structure in Casper:

HTML view of the post content highlighting the post content wrapping element

Once you’ve added the script above and set the right selector for the content click save in the top right of the view.

The other selector in the script you’ve added, .toc, is the element where we want the table of contents to render. Navigate to a post in Ghost admin and create a new HTML card in the area you want your table of contents to appear. A HTML card can be added using the “+” button or by typing “/HTML” and hitting enter.

HTML card being highlighted in card options

Within the HTML card add the following code:

<aside class="toc"></aside>

Then click save and view the post. The element you’ve added to the page will now contain a table of contents created from all the headings in your content 🙌.

A table of contents being rendered above the post content

Adding a table of contents to your theme

While using code injection is a nice quick way to add a table of contents to your posts, what if you’re wanting to add it more permanently to all your articles? And what if you’re wanting to customise your table of contents with style and layout? Let’s leverage the Ghost theming layer and download your theme to add a customised table of contents.

Take the link tag for the Tocbot CSS and move it to within the <head> tag within your theme. For Casper theme this can be found in the default.hbs file Make sure to add this before the main CSS file of your theme, this will mean we can overwrite it’s default styles in our own theme styles:

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.10.0/tocbot.css">
    <link rel="stylesheet" type="text/css" href="{{asset "built/screen.css"}}" />
    {{ghost_head}}

Pro tip: More advanced developers can use the asset helper and add Tocbot directly into their theme assets.

Next take the script tag for the Tocbot JavaScript and move it to just before the closing </body> tag. This can also be found in the default.hbs file within Casper. Along with the script tag move the initialising script to the same location in your theme. Here’s an example:

<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.10.0/tocbot.min.js"></script>
<script>
    tocbot.init({
        tocSelector: '.toc',
        contentSelector: '.post-content',
        hasInnerContainers: true
    });
</script>
{{{block "scripts"}}}
{{ghost_foot}}
</body>

Editing the post template

Now that our theme has been setup to create a table of contents we can select an area for it to render. In my example I want a table of contents to appear just before the main content on all my posts, so I’ll add the .toc element just before the .post-content wrapper like so:

<section class="post-full-content">
    <aside class="toc-container">
        <div class="toc"></div>
    </aside>
    <div class="post-content">
        {{content}}
    </div>
</section>

Styling the table of contents

The final step is to add some CSS styles to make the table of contents fit in with our custom theme. I used the following to fit my table of contents into my customised version of Casper:

/* Offset headings from fixed header */
.post-content h2,
.post-content h3 {
    scroll-margin-top: 84px;
}

/* Adjust content wrapper */
.post-content {
    display: block;
}

/* Adjustments to wide and full width cards */
.kg-gallery-card,
.kg-width-wide,
.kg-width-full {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.kg-gallery-card > *,
.kg-width-wide > *,
.kg-width-full > *,
figure.kg-width-full img {
    margin-left: -50vw;
    margin-right: -50vw;
}

.post-full-content pre {
    max-width: 0;
}

You might not need all of the CSS shown above, here’s a brief breakdown on what each part does:

  • The first offsets the headings from the top of the screen. This keeps the headings visible if you have a fixed header on your site, as seen in Casper. Change the two numbers (84px and -84px) to suit your needs.
  • The second part is for your wrapping element, the element that immediately wraps the post and page content. This needs to be display block in order for the heading offset to work. This needs to be set in Casper.
  • The final part is optional for most sites but is needed for Casper. The wide and full width cards require some additional adjustments to compensate for the display block change on the wrapping element, .post-content.

To find out more about adding styles to Casper specifically, check out the documentation for Casper on GitHub.

Upload and test

Once you’ve added the code to your theme and made your own desired adjustments - zip the theme up and upload it via the Design view in Ghost admin. Here’s an example in action:

A table of contents being clicked on and the view scrolling down to the relevant section

Advanced styling

CSS is an extremely powerful tool within Ghost theme customisation, giving us control over presentation and layout. Take this design for example:

Example of a table of contents beside a post

The “sticky” table of contents navigation and adjusted layout was all achieved without modifying the HTML, only by adding CSS on top of existing styles. If you’re wanting to achieve something similar add the following code to main styles file within your custom theme:

.post-content h2,
.post-content h3 {
    outline: none;
}
body {
    overflow: visible;
}
.post-full-content {
    display: grid;
    grid-template-columns: 1fr 0.2fr;
    padding: 0 0 6vw;
    margin: 0;
}
.toc-container {
    order: 1;
}
.toc-container .toc {
    position: sticky;
    top: 70px;
    min-width: 260px;
    font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Open Sans, Helvetica Neue, sans-serif;
    font-size: 1.6rem;
    line-height: 1.6em;
    padding: 0 0.8em;
}
.is-active-link::before {
    background-color: #3eb0ef;
}
ol.toc-list {
    margin: 0;
}

Summary

Your Ghost site must be looking pretty sharp now ✨. Having the ability to customise themes to suit your needs is a really powerful tool. Thanks to the Ghost Handlebars theming layer we’re able to utilise standard HTML, CSS and JavaScript to enhance the viewing experience of our sites. Check out more of our tutorials and guides to help you cater your Ghost site to your audience needs.