How to Add a Table of Contents to WordPress Posts
When a table of contents helps a post and when it is clutter, three ways to add one in WordPress, and how to keep its anchor links from breaking.
In this article 11 sections
- What a table of contents is made of
- When it helps, and when it does not
- What it does for search
- Method 1: write the links by hand
- Method 2: a block or shortcode that builds the list for you
- Method 3: your theme or page builder
- Which heading levels to include
- Where to put it
- The details that make it work well
- Common problems
- A short checklist
A table of contents is a list of links near the top of a page, one for each section, that takes the reader straight to the part they want. On a long guide it is the difference between scanning thirty screens and tapping once. On a short post it is clutter. This guide covers when a table of contents earns its place, the three ways to add one to a WordPress post, and the details that decide whether it works well: anchors, heading levels, placement and what happens when a heading is renamed.
What a table of contents is made of
Two parts, and both have to be right:
- A list of links, each ending in a fragment such as
#storing-sourdough. - A matching id on each heading, such as
<h2 id="storing-sourdough">.
When someone follows the link, the browser scrolls to the element with that id. If the id is missing or spelled differently, nothing happens. Most broken tables of contents are a mismatch between the two.
Because the links are ordinary fragment links, the same anchors work from anywhere: an email, another post, a search result. A table of contents gives every section of the page its own address.
When it helps, and when it does not
A table of contents is worth adding when a reader is likely to want one part of the page and not the rest:
- long guides and tutorials with five or more main sections
- reference pages and documentation people come back to
- comparison pages, where readers jump to the option they are considering
- long FAQ pages
Leave it off when the page is short, when it is read from top to bottom like a story or a news item, or when it is a landing or product page where the list would push the offer further down. A list of three links above a 500-word post tells the reader nothing they could not see by scrolling once.
A fair test: open the post on a phone. If you have to scroll more than a few screens to see what the page covers, a table of contents helps.
What it does for search
Be precise about this, because a lot of advice is not.
- It is not a ranking factor. Adding a table of contents does not move a page up.
- It can lead to section links under your result. Google sometimes shows links to parts of a page beneath the main result when the page has clear sections with anchors. Google decides whether to show them, and no markup guarantees it.
- It describes the page. The links are internal links with descriptive anchor text that name each section and give anything that reads the page a compact outline of it.
- It helps readers find what they came for, which is the reliable benefit and reason enough.
A table of contents is only as good as the headings behind it. If the headings are vague ("Overview", "More details") the list is vague too. Run the page through our free heading structure checker first: it shows the outline as a reader of the list will see it, with skipped levels and empty headings flagged.
Method 1: write the links by hand
No plugin is needed for a single page.
- In the block editor, select a Heading block, open Advanced in the sidebar and type an id into HTML anchor, for example
storing-sourdough. Use lowercase words joined by hyphens, no spaces. - Repeat for each heading you want to list.
- At the top of the post, add a List block and link each item to
#followed by the anchor:#storing-sourdough.
This gives you full control, and anchors that never change unless you change them. The cost is upkeep: add a section and you must remember to add it to the list; reorder sections and the list is out of order. It suits one or two cornerstone pages, not a blog.
If you would rather not type the list, paste the post's HTML into our free table of contents generator. It returns the list and the headings with ids, ready to paste back.
Method 2: a block or shortcode that builds the list for you
The practical choice for a site with many long posts is a block that reads the headings each time the page is shown, so the list can never be out of date.
Hydrogen SEO has one from version 0.0.31. In the block editor:
- Put the cursor after the introduction.
- Insert the Table of Contents block. It is in the Design category, and typing
/tocfinds it. - In the sidebar, set the Title, tick the Heading levels to include (H2 and H3 by default) and choose a List style, numbered or bulleted.
In the Classic editor or a page builder, the shortcode does the same:
[hydrogen_seo_toc title="In this guide" levels="2-4" list_style="bulleted"]
The block lists the headings in order, nested by level, and gives each one that lacks an id an anchor made from its text: "Storing sourdough" becomes storing-sourdough, and a second heading with the same text gets -2. Headings that already have an HTML anchor keep it. To leave a heading out, add the CSS class hydrogen-seo-toc-exclude to it.
Two properties matter if you care about what your pages load. It adds no JavaScript, and it touches only posts that contain the block or shortcode: other pages are not parsed and their headings get no ids. The full reference is in table of contents block and shortcode.
One limit to know: the preview in the editor is built from the post's Heading blocks. The published list is built from the finished page, so if a shortcode or another block prints headings, the published list will include them and the preview will not.
Method 3: your theme or page builder
Some themes and page builders include a table of contents element. If yours does and you are happy with it, there is no need to add another. Check three things: that it adds ids only where needed, that it works without JavaScript or degrades to plain links, and whether its anchors survive a heading being renamed.
Do not run two at once. Two systems writing ids on the same headings is how links end up pointing at the wrong section.
Which heading levels to include
- H2 only for posts with a handful of sections.
- H2 and H3 for most guides: main sections with one level of detail.
- H4 and deeper for documentation and reference pages. On an article it produces a list longer than anyone will scan.
If the list is longer than about one screen on a phone, cut a level. The table of contents is for navigation, and it does not have to mirror the full outline. The H1 is the page title and does not belong in the list.
Where to put it
After the introduction and before the first H2. The opening paragraph tells the reader they are in the right place; the list then shows what is on offer. A table of contents above the introduction pushes your opening, the part that tells a visitor what the page is, further down the screen.
On very long pages, some sites repeat a "back to top" link at the end of each section. It is cheap and readers on phones use it.
The details that make it work well
Stable anchors. A generated anchor follows the heading text, so renaming "Storing sourdough" to "How to store sourdough" changes #storing-sourdough to #how-to-store-sourdough, and anyone who saved the old link lands at the top of the page. For sections that are likely to be linked to, set the HTML anchor by hand. It then stays the same however the heading is reworded.
Sticky headers. If your site's header stays at the top of the screen, a heading can end up hidden beneath it after a jump. One CSS rule fixes it:
h2, h3 { scroll-margin-top: 80px; }
Set the value to your header's height.
Smooth scrolling. Optional, and best limited to people who have not asked for reduced motion:
@media (prefers-reduced-motion: no-preference) {
html { scroll-behavior: smooth; }
}
Accessible markup. The list should sit in a <nav> element with a label, so screen reader users can tell it from the main menu and skip it. A numbered list fits a page that is read in order; bullets fit a reference page.
Headings that say something. "Step 3: proof overnight in the fridge" works as a link. "Step 3" does not.
Common problems
The links do nothing. The headings have no ids, or the ids do not match. View the page source and compare a link's fragment with the id on its heading.
A link jumps to the wrong place. Two elements share an id. An id may appear once on a page; give the second one a different anchor.
The heading is hidden after the jump. The sticky header is covering it. Add scroll-margin-top.
A heading is missing from the list. Its level is not included, or it is not a real heading: bold text in a paragraph looks like a heading and is not one. The heading structure checker shows which is which.
The list appears in the post excerpt or feed. It should not. A well-built block prints nothing there; a hand-written list will, since it is part of the content.
A short checklist
- The post is long enough to need one.
- Headings are descriptive and in a sensible hierarchy.
- The list sits after the introduction.
- H2 and H3 only, unless the page is reference material.
- Sections you expect to be linked to have hand-set anchors.
- Tested on a phone, with one tap on the first and last entries.
Further reading: internal linking strategy for links between pages, and the featured snippet glossary entry for how clearly labelled sections get quoted in search results.