How to place a table of contents in the WordPress sidebar

As the average length of content on the internet gets longer (it’s now 1,696 words according to this new study, which is a 48% increase from 2018), user experience for navigating content is becoming more and more important. No one likes reading a huge wall of text or needing to scroll through a huge article in order to find the one piece of information they’re looking for.

This is exactly why, after all, Google created Featured Snippets in the mid 2010s to give searchers direct answers for what they are searching for directly in the search results!

We also see that search engines prefer to rank long content, because a long piece of content is much more likely to cover the full topic than a shorter piece is.

To balance these competing demands – of readers looking to find specific information (or read about a full topic) and search engines preferring longer content and thus that’s what creators should give them – we need to find ways to make both work.

One of the best ways to help readers is a table of contents. When an article is reaching short book length, a table of contents helps organize the piece. On the web, it also helps users navigate.

I wrote before about how to create a table of contents in WordPress, but today I’m going to show you how to place a table of contents in the WordPress sidebar.

Why a table of contents in WordPress?

There are many good reasons to include a table of contents with your blog posts, and it’s quite easy to do with WordPress.

A table of contents accomplishes the following goals:

  1. Organizes the post into sections for users to use to navigate if needed;
  2. Helps the writer organize their thoughts which makes for a stronger piece;
  3. Can help SEO when the search engines use the table of contents to give a search listing a secondary section of links.

A table of contents is becoming best practice on the web, especially when content is becoming longer and longer by the year. Implementing one, which is very easy to do in WordPress with a simple lightweight plugin or some code if you’re more technically inclined, can help with your on-page metrics such as time on site and bounce rate, which in turn often correlate directly to better conversions and even social shares.

Why a table of contents in the WordPress sidebar?

For many WordPress sites, the main driver of traffic is content. WordPress started after all as a blogging platform before it expanded and has become many things, including memberships and e-commerce. I’ve even built a few full fledged web apps using WordPress plugins with a membership plugin to control access.

For sites that have a lot of desktop traffic, a sidebar table of contents can serve the multiple purposes of:

  1. Providing a table of contents;
  2. Keeping the main content as high on the page as possible to provide a better user experience with less scrolling;
  3. If you’re feeling fancy, having a sticky table of contents so that users can use it to navigate even when they’re further down the page from the top.

It’s important to note that a sidebar table of contents won’t do your mobile users much good. For most WordPress themes, the default location of a sidebar is at the bottom of a blog post. While it may be argued (and I think correctly) that users who arrive at the end of your post may want to easily navigate back up, a table of contents will probably be more useful at the top of the page for mobile users.

There are therefore a few ways you can handle a table of contents for mobile:

  1. Hide the table of contents on mobile, as our friend Chris does;
  2. Implement an in-content table of contents for mobile users; or
  3. Implement the in-content table of contents and keep the sidebar table of contents to appear at the bottom.

My choice would be (and is) the 3rd, but you should do what you believe is right for your users.

How to place a table of contents in the WordPress sidebar

When placing a table of contents in the WordPress sidebar, there are two main ways to do it:

  1. With a plugin
  2. With custom code

How to implement a sidebar table of contents with a WordPress plugin

Implementing a sidebar table of contents is fortunately quite easy to do in WordPress with a simple plugin.

The easiest way to do it is to use a plugin that also includes a shortcode that can be placed as a widget in the sidebar to show up on single posts.

For this, our plugin of choice is LuckyWP Table of Contents. It is a straight forward plugin that also provides an easy shortcode that is simply

. More on that in a bit.

First, navigate to your Plugins page which is found at yourdomain.com/wp-admin/plugins.php. Of course, substitute your domain for yourdomain.com!

Important: before installing any plugins it is recommended to back up your website with your host (we use Kinsta here at EditorNinja) and then install any new plugins on a staging site/environment before installing them on your public live production site. This is so that you do not take your main website offline if there are any plugin conflicts that need to be resolved first.

After navigating to your Plugins page, click the Add New button near the top right beside the title of the page:

To find the LuckyWP Table of Contents plugin, search “table of contents” in the search bar at the top right:

For me, it was the second option down on the far left (so overall the 4th option).

Click Install Now and then Activate. Once that is done, go back to your Plugins page and find “LuckyWP Table of Contents” and click Settings:

The only things I changed in the Settings are:

  1. I changed the “Title” section from “Contents” to “Table of Contents”;
  2. I made sure “Auto Insert” was not enabled, as I do not want it automatically inserted into my content (as I use the shortcode in the sidebar);
  3. Under the “SEO” section on the “Misc.” tab, I checked the “Use rel=”nofollow” for links” checkbox as I do not want to send link equity to those sections of the page.

If you are using a theme that has sidebars on pages other than blog posts, you will also want to control the pages on which the Table of Contents displays. You will likely want to constrain it to just Posts, though if you’re using Custom Post Types you may want to use them there as well.

Now that the plugin is in place, it is time to put the table of content into the sidebar.

Using a shortcode to show table of contents in the WordPress sidebar

If you’re using the LuckyWP Table of Contents plugin, it is easy to add a widget in your site’s sidebar with a simple widget plus the shortcode.

Navigate to Appearances -> Widgets in the sidebar navigation menu of your WP-ADMIN.

Once there, assuming you’re using the Block editor with WordPress Gutenberg, click the + and find the “Shortcode” block:

Next, add the LuckyWP shortcode

to that block, then click the blue Update button in the top right. Of course, position the widget block wherever you want it to appear in your side navigation.

Now navigate to a blog post and make sure it shows, for example:

How to stick your table of contents in the sidebar as your users scroll

The next step some of you might want to take is making the table of contents sticky so that it scrolls with your site visitor.

To do this, you can use the WP Sticky Sidebar plugin. This solution usually works out of the box, and you control which widget is sticky on the widget within the Appearance -> Widgets section and then the individual widget.

Note that your user experience (UX) can get strange if you make a widget other than the bottom one sticky. A way to hack around this is to display two Table of Contents widgets, one near the top and one at the bottom of the sidebar, and just make the bottom one sticky.

How to hide a sidebar table of contents from mobile users

As mentioned above, we need to take both desktop and mobile users into account. For a long time most web traffic was desktop, but November 2016 was the time that web traffic from mobile was more than 50%. The “year of mobile” never came, but rather snuck up on us like a hunting tiger.

Find your percentage of traffic that is mobile

To find the percentage of your traffic that is mobile, the simplest way is to use Google Analytics and navigate to Audience -> Mobile -> Overview. Here’s EditorNinja’s for the period back to the beginning of December:

For us mobile is just 25%, so I am going to optimize for desktop users. But if your traffic is 50%+ and you’re optimizing for mobile, you can use the Widget Options plugin to control at a granular level on which pages or posts a widget will show, but also based on type of traffic (desktop or mobile). To hide your Table of Contents on mobile, because it will likely by default show at the bottom, you can set that widget to not show for mobile traffic. It’s what our friend Chris does, and our friend Chris is usually right.

If you want to get really fancy with your table of contents and mobile, you could also write some custom code to only show an in-content table of contents to mobile users and the sidebar to desktop users.

How to implement a sidebar table of contents in WordPress with custom code

While most people want the easy answer to creating a table of contents and thus will use the widget and shortcode option, there are purists who prefer to do it manually or with code.

I’ll be honest – it is not simple to do it with custom code and I’m not really sure why someone would do it unless they have a lot of plugins already and their site’s performance is suffering (which is common with WordPress sites with a lot of plugins).

But if you want to do it, here’s how.

For this we rely on the trusted folks at WebDeasy, who provide the following code:

function get_toc($content) {
  // get headlines
  $headings = get_headings($content);

  // parse toc
  ob_start();
  echo "<div class='table-of-contents'>";
  echo "<span class='toc-headline'>Table Of Contents</span>";
  echo "<!-- Table of contents by webdeasy.de -->";
  echo "<span class='toggle-toc custom-setting' title='collapse'>−</span>";
  parse_toc($headings, 0, 0);
  echo "</div>";
  return ob_get_clean();
}

function parse_toc($headings, $index, $recursive_counter) {
  // prevent errors
  if($recursive_counter > 60 || !count($headings)) return;

  // get all needed elements
  $last_element = $index > 0 ? $headings[$index - 1] : NULL;
  $current_element = $headings[$index];
  $next_element = $index < count($headings) ? $headings[$index + 1] : NULL;

  // end recursive calls
  if($current_element == NULL) return;


  // get all needed variables
  $tag = intval($headings[$index]["tag"]);
  $id = $headings[$index]["id"];
  $classes = $headings[$index]["classes"];
  $name = $headings[$index]["name"];


  // element not in toc
  if($current_element["classes"] && in_array("nitoc", $current_element["classes"])) {
    parse_toc($headings, $index + 1, $recursive_counter + 1);
    return;
  }


  // parse toc begin or toc subpart begin
  if($last_element == NULL || $last_element["tag"] < $tag) echo "<ul>";


  // build li class
  $li_classes = "";
  if($current_element["classes"] && in_array("toc-bold", $current_element["classes"])) $li_classes = " class='bold'";

  // parse line begin
  echo "<li" . $li_classes .">";

  // only parse name, when li is not bold
  if($current_element["classes"] && in_array("toc-bold", $current_element["classes"])) {
    echo $name;
  } else {
    echo "<a href='#" . $id . "'>" . $name . "</a>";
  }

  if(intval($next_element["tag"]) > $tag) {
    parse_toc($headings, $index + 1, $recursive_counter + 1);
  }

  // parse line end
  echo "</li>";

  // parse next line
  if(intval($next_element["tag"]) == $tag) {
    parse_toc($headings, $index + 1, $recursive_counter + 1);
  }


  // parse toc end or toc subpart end
  if($next_element == NULL || $next_element["tag"] < $tag) echo "</ul>";

  // parse top subpart
  if($next_element != NULL && $next_element["tag"] < $tag) {
    parse_toc($headings, $index + 1, $recursive_counter + 1);
  }
}

function get_headings($content) {
  $headings = array();
  preg_match_all("/<h([1-6])(.*)>(.*)<\/h[1-6]>/", $content, $matches);
  
  for($i = 0; $i < count($matches[1]); $i++) {

    $headings[$i]["tag"] = $matches[1][$i];

    // get id
    $att_string = $matches[2][$i];
    preg_match("/id=\"([^\"]*)\"/", $att_string , $id_matches);
    $headings[$i]["id"] = $id_matches[1];

    // get classes
    $att_string = $matches[2][$i];
    preg_match_all("/class=\"([^\"]*)\"/", $att_string , $class_matches);
    for($j = 0; $j < count($class_matches[1]); $j++) {
      $headings[$i]["classes"][] = $class_matches[1][$j];
    }

    $headings[$i]["name"] = $matches[3][$i];
  }

  return $headings;
}

You should insert the above in your theme’s functions.php file (or more accurately, your theme’s child theme’s functions.php so it is not overwritten when you update your theme).

Please please please remember to backup your site and test changes in a staging environment before rolling them live to your site. We are not WordPress support specialists at EditorNinja and cannot answer any questions about why the above code may not work in your unique environment.

To be able to use the above to code to output the table of contents into the sidebar, you should also add this code:

// TOC (from webdeasy.de)
function toc_shortcode() {
    return get_toc(get_the_content());
}
add_shortcode('TOC', 'toc_shortcode');

That code will create a [TOC] shortcode for use using the steps given above for using a shortcode to output a table of contents in a widget in your site’s sidebar.

You can use name the shortcode anything by changing the ‘TOC’ in this line:

add_shortcode(‘TOC’, ‘toc_shortcode’);

Conclusion

After reading this article, you are now able to implement a table of contents in the sidebar of your WordPress site using both a plugin and code and a shortcode.

Go forth and write and provide your readers a better experience!