jekyll-toc-generator

Hook-based table of contents injection for Jekyll. Posts and pages with toc: true in frontmatter automatically get a TOC injected — no Liquid tag required in the content.

Installation

Add to your Jekyll site's Gemfile:

gem "jekyll-toc-generator"

Then add to _config.yml:

plugins:
  - jekyll-toc-generator

Run bundle install.

Usage

Add toc: true to any post or page frontmatter:

---
title: My Long Post
toc: true
---

The plugin automatically injects a <nav class="toc"> element immediately before the first <h2> in the rendered HTML. No Liquid tag needed.

Configuration

All options are optional. Add to _config.yml:

toc:
  min_headings: 2      # don't inject TOC if fewer than N h2s (default: 2)
  title: "Contents"   # TOC nav title (default: "Table of Contents")
  levels: [2, 3]       # heading levels to include (default: [2, 3])

Options

Option Default Description
min_headings 2 Minimum number of <h2> elements required to inject a TOC
title "Table of Contents" Text shown in the TOC <h2> title
levels [2, 3] Heading levels to include in the TOC

TOC HTML Structure

<nav class="toc" aria-label="Table of contents">
  <h2 class="toc__title">Table of Contents</h2>
  <ul class="toc__list">
    <li class="toc__item"><a href="#heading-slug">Heading Text</a>
      <ul class="toc__sublist">
        <li class="toc__item--sub"><a href="#sub-heading-slug">Sub Heading</a></li>
      </ul>
    </li>
  </ul>
</nav>

CSS Classes

Class Element Description
.toc <nav> Outer TOC container
.toc__title <h2> TOC section title
.toc__list <ul> Top-level heading list
.toc__item <li> Top-level heading item
.toc__sublist <ul> Nested subheading list
.toc__item--sub <li> Subheading item

Example CSS

.toc {
  background: #f8f8f8;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 1rem 1.5rem;
  margin-bottom: 2rem;
  display: inline-block;
  min-width: 200px;
}

.toc__title {
  font-size: 1rem;
  margin: 0 0 0.5rem;
  font-weight: 600;
}

.toc__list,
.toc__sublist {
  list-style: none;
  margin: 0;
  padding: 0;
}

.toc__sublist {
  padding-left: 1rem;
}

.toc__item,
.toc__item--sub {
  margin: 0.25rem 0;
}

Slug Generation

Heading IDs are slugified to match Jekyll's default anchor generation:

  • Lowercase
  • Non-alphanumeric characters removed
  • Spaces and underscores replaced with -
  • Leading/trailing hyphens stripped

Example: "What's New?"#whats-new

Requirements

  • Jekyll >= 4.0
  • Ruby >= 2.7
  • No additional gem dependencies (pure Ruby, no Nokogiri)

License

MIT © Jason Chance 2025