Add Table of Contents

Use Bengal's auto-generated page.toc for navigation

2 min read 343 words

Bengal automatically generates a table of contents from page headings. Access it viapage.toc.

The Pattern

{% if page.toc %}
<nav class="toc" aria-label="On this page">
  <h2>On this page</h2>
  {{ page.toc | safe }}
</nav>
{% end %}

That's it. Bengal parses headings and generates nested HTML lists.

What's Happening

Component Purpose
page.toc Pre-rendered HTML list of headings
| safe Render as HTML, not escaped text

Control Which Headings

Configure TOC depth inbengal.toml:

[content]
toc_depth = 4  # Maximum heading depth (1-6). Default: 4

This controls how deep the TOC goes. For example:

  • toc_depth = 2includes only H2 headings
  • toc_depth = 3includes H2 and H3 headings
  • toc_depth = 4includes H2, H3, and H4 headings (default)

For parser-specific control, usemarkdown.toc_depthwith a range string:

[markdown]
toc_depth = "2-4"  # String format for parser-level control

Variations

---
title: Simple Page
toc: false
---

Then in template:

{% if page.toc and page.metadata.toc != false %}
  {{ page.toc | safe }}
{% end %}
<div class="article-layout">
  <aside class="sidebar">
    {% if page.toc %}
    <nav class="toc">
      <h2>On this page</h2>
      {{ page.toc | safe }}
    </nav>
    {% end %}
  </aside>

  <main>
    <h1>{{ page.title }}</h1>
    {{ page.rendered_html | safe }}
  </main>
</div>
{# Only show TOC for docs section #}
{% if page.section == 'docs' and page.toc %}
  <nav class="toc">
    {{ page.toc | safe }}
  </nav>
{% end %}

Scroll Highlighting (JavaScript)

Highlight current section as user scrolls — this part is standard JS, not Bengal-specific:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      document.querySelectorAll('.toc a').forEach(a => a.classList.remove('active'));
      document.querySelector(`.toc a[href="#${entry.target.id}"]`)?.classList.add('active');
    }
  });
}, { rootMargin: '-20% 0px -80% 0px' });

document.querySelectorAll('h2[id], h3[id]').forEach(h => observer.observe(h));

Seealso