Skip to main content

Reading time: five minutes. The list is built from the post’s content and styled by your theme, which is what makes it fit in on almost every site without being told to. This page covers the places where it needs a hand.

Themes

The list ships with the least CSS we could manage: the layout of the list itself, the fold, and the four optional boxes. The typeface, the sizes, the text colour and the link colour all come from your theme, so the list looks like the rest of the page rather than like a plugin.

Every release is tested in a browser against Storefront, Astra, Kadence, GeneratePress and WordPress’s current default theme, in both directions of text, with and without JavaScript. The two things that pass or fail there are the indentation and the colours, and both are things a theme can get in the way of.

Three settings cover nearly everything:

  • Box. “None” lets your theme decide everything, which is right on a theme with a definite look. If the list ends up looking bare in the middle of a styled page, “Light” or “Dark” gives it a panel.
  • Extra CSS classes. Adds your own class to the list so you can style it in your theme without touching the plugin.
  • Load no stylesheet. Prints no CSS from us at all. The list is still a proper nested list of proper links and the fold still works; you take over the appearance completely.

Themes with a bar fixed to the top of the screen will cover the heading your reader just jumped to. Set Fixed header offset to the height of that bar in pixels. Measure it rather than guess; 80 is a reasonable first try.

Use the Table of Contents widget in Appearance, then Widgets. It lists the post being read, so it is empty on pages that are not a single post.

Page builders

Every builder here can hold a shortcode, and the shortcode is the route we support and test:

“`
[essence_toc]
“`

Everything about it is optional and anything you leave out comes from your settings. Every setting explained lists the attributes.

The reason for the shortcode rather than a widget per builder is honest: a builder-specific widget for five builders is five things to keep working against five release schedules, and the shortcode does the same job in one.

Add a Shortcode widget where you want the list, and put [essence_toc] in it.

Automatic placement also works on any Elementor template that outputs the post content through the Post Content widget, which single-post templates normally do. If your template builds the page out of separate text widgets instead, the post has no content for us to read, and the shortcode is the answer.

Add a Text module and put [essence_toc] in it, or use the Code module. The same applies as with Elementor: automatic placement works where the layout renders post content, and the shortcode works everywhere.

Add a Text element containing the shortcode. In a dynamic page template, automatic placement works where the template outputs the post content element.

Not in our test matrix, and all four can hold a shortcode. If automatic placement does not appear, use [essence_toc] in the builder’s own text or shortcode element.

The Table of contents block works anywhere in a post or page. In a template, put it inside the part of the template that has a post to read, which in practice means inside the Post Template or the single-post template rather than in a header.

Caching plugins

The list is part of the page’s HTML, so it caches with the page and there is nothing to configure. We send no JavaScript and fetch no file, so there is nothing to exclude from minification, nothing to defer, and nothing to add to a “do not combine” list.

One thing worth knowing: because the stylesheet is printed into the page rather than linked as a file, a plugin that moves all CSS into one combined file may move ours too. That is fine. If a plugin strips <style> elements from the body, the list will lose its layout, and Load no stylesheet plus your own CSS is then the cleaner arrangement.

Other table of contents plugins

Only run one. Two plugins both adding ids to your headings will fight over the names, and the loser’s links break.

If you are moving, the two guides below do it properly, keeping every link anyone has shared:

Translation plugins

The list is built from whichever version of the post is being shown, so a translated post gets a list of its own translated headings, with anchors made from them. This works with WPML, Polylang and TranslatePress because none of them needs anything from us: they change the content, and we read the content.

The list’s title is translatable in the usual way, and the plugin’s own strings are on wordpress.org’s translation site.

What next