Create WordPress table of contents automatically – without plugin!

Create WordPress table of contents automatically – without plugin! Thumbnail
Published on 19. May 2020Last updated on 17. April 2021

With many WordPress blog entries or pages, a table of contents can work wonders and give the visitor a quick overview of the structure of the content.


It is simply too cumbersome to create a new table of contents in every post or to update it when changes occur. Installing a plugin (e.g. Easy Table Of Contents) for such a “little thing” is also not a good solution, as it only slows down the site and – as you will see in a moment – you can easily code it yourself.

If you have little or no programming experience, you can still use this table of contents. This is no problem! If there are any questions left, I can also try to help you with the comments.

This is what our end result will look like:

The complete code is programmed by myself and may be used free of charge for any purpose (also commercial) and may be modified as desired!

1. Generate table of contents (PHP script)

The following code generates the table of contents from the headings <h2> and <h3>.


For experienced programmers: This code must be included in the functions.php of the used theme (if used in the child theme).

For inexperienced programmers: In the backend menu (left margin) there is an item “Appearance”. Navigate there to the submenu item “Theme Editor” and then select the “Theme Functions” (functions.php) on the right side under “Theme Files”. There you can scroll to the very end and after the last line of code insert 2-3 empty lines and then insert and save the PHP code below 1:1.

// filter function to generate the table of content (from
function get_table_of_content($content) {

    preg_match_all("/<h[2,3](?:\sid=\"(.*)\")?(?:.*)?>(.*)<\/h[2,3]>/", $content, $matches);
    $tags = $matches[0];
    $ids = $matches[1];
    $names = $matches[2];

    <ul class="table-of-contents">
        <!-- Table of contents by (LH) -->
        <?php for($i = 0; $i < count($names); $i++) { ?>
            <?php if(strpos($tags[$i], "h2") === false || strpos($tags[$i], "class=\"nitoc\"") !== false) continue; ?>
                    <?php if(!empty($ids[$i])) { ?>
                        <a href="#<?php echo $ids[$i]; ?>"><?php echo $names[$i]; ?></a>
                    <?php } else { ?>
                        <?php echo $names[$i]; ?>  
                    <?php } ?>
                    <?php if($i !== count($names) && strpos($tags[$i+1], "h3") !== false) { ?>
                            <?php for($j = 0; $j < count($names) - 1; $j++) { ?>
                                <?php $sub_index = $i + $j; ?>
                                <?php if($j != 0 && strpos($tags[$sub_index], "h2") !== false) break; ?>
                                <?php if(strpos($tags[$sub_index], "h3") === false || strpos($tags[$sub_index], "class=\"nitoc\"") !== false) continue; ?>

                                    <?php if(!empty($ids[$sub_index])) { ?>
                                        <a href="#<?php echo $ids[$sub_index]; ?>"><?php echo $names[$sub_index]; ?></a>
                                    <?php } else { ?>
                                        <?php echo $names[$sub_index]; ?>  
                                    <?php } ?>
                            <?php } ?>
                    <?php } ?>

        <?php } ?>
    return ob_get_clean();

You can include the table of contents in your posts in two different ways.

Option 1: If you want to set the position in each post yourself using the placeholder {{TABLE_OF_CONTENTS}}, then add this code as well:

function add_table_of_content($content) {
    return str_replace("<p>{{TABLE_OF_CONTENTS}}</p>", get_table_of_content($content), $content);
// add our table of contents filter (from
add_filter('the_content', 'add_table_of_content');

Option 2: If you want the table of contents to be inserted automatically after the first paragraph in each post, then add this code as well:

function add_table_of_content($content) {
	if (!is_single()) return $content;

    $paragraphs = explode("</p>", $content);
    $paragraphs_count = count($paragraphs);
    $middle_index= absint(floor($paragraphs_count / 2));
    $new_content = '';

    for ($i = 0; $i < $paragraphs_count; $i++) {
        if ($i === 1) {
        	$new_content .= get_table_of_content($content);

        $new_content .= $paragraphs[$i] . "</p>";
    return $new_content;
// add our table of contents filter (from
add_filter('the_content', 'add_table_of_content');

If you want to output the table of contents only after the second paragraph, you can simply adjust the number in line 10.

And one more thing about the technical background:


The regular expression for reading the headings is structured as follows:


With this we search for all HTML tags that are an h2 or h3. The part (?:\sid=\"(.)\")?(?:.)? groups us the ID (if available) and also groups all other attributes (if available) that the HTML tag still has.

A little tip: If you want to earn some money on the side with your website or blog, this article will tell you everything you need to know about Google Adsense, one of the largest advertising services on the Internet.

2. Pretty up the table of contents (CSS)

Your table of contents is already generated with the previous code. But it would be output as a normal list <ul>. To make the whole thing look a bit nicer, we also add a few lines of CSS.

For experienced programmers: This code must be inserted into a CSS file of the used theme (if used in the child theme). Often this is the top level style.css in the theme folder by default.

For inexperienced programmers: In the same menu item as with the PHP code, you can simply select “Stylesheet” (style.css) on the right. There you also go back to the end and add the following code after the last line and then save the file.

ul.table-of-contents {
  padding: 1rem;
  border-left: 3px solid #cecece;
  background-color: #e6e6e6;
ul.table-of-contents > li:first-child {
    margin-bottom: 0.5rem;
    font-size: 110%;
table-of-contents li {
  list-style: none;

Now the table of contents looks better and is also recognizable as such. Of course, you can adapt everything to your theme and your wishes.

3. Include directory in individual contributions

If you have chosen option 1, to output the table of contents now on a page, you simply have to insert {{TABLE_OF_CONTENTS}} as a normal paragraph in the backend editor of the respective post.

Table of contents selector in the Backend view

In addition, you should provide each heading with an HTML anchor. To do this, click on the headline and add an HTML anchor under “Advanced”. This anchor is then attached to the URL with a hash (#) and allows you to jump to the table of contents. You are free to choose the name, but it should match the headline.

Advanced Headline menu

That’s all! If you look at the page now, the table of contents should be output according to the <h2> and <h3> tags.

Exclude menu items

If you want to exclude one or more menu items (like this headline :)), so that they are not displayed in the table of contents, you can additionally give the respective headline the CSS class nitoc. The script will then ignore them.


You did it! With just a few lines of code you can now always generate your own table of contents and don’t have to resort to ready-made plugins anymore. Or even worse: always write and update the tables of contents yourself. Have fun with it! 🙂

Related Posts
💬 Join the Conversation


  1. yf says:

    Great function. But how have exactly autolinking? The render html don´t have a href with link. Can you update little function child for that please.

    p.s; i tried function “* Automatically add IDs to headings such as” but not success, when i see inspector, i see the li but nothing about ahref=”link to the section”

    1. LH says:

      Hey, next time when I update this post I will check if I can add this feature.


      1. yf says:

        Thanks. Can you paste some tips quick tips code to do that please and replease, a child function for this option.

        1. yf says:

          if posible before update the post of course

          1. yf says:

            I forgot: seem you have a conflict when we use special tags blog spintax from plugin page generator

            i believe is for that use ‘ # ‘ (example #p#text text #/p)

  2. Hello friend, I tried lots of coding to display h4 headings in the “table of content”, but failed, kindly provide the code display h4 headings as well. I need help friend.

    1. LH says:

      In the comments of the german version of this post (click here) has someone posted a recursive version of the code (including h4). You can try this code. Someday I will create my own (improved) version – until you can you his code. 🙂

  3. Thanks for this code buddy, but i want to know how to display output means “table of content” in single.php file, what will be the code for single.php file to include to display table of content. I don’t want to write {{TABLE_OF_CONTENTS}} again and again in every post.

    1. LH says:

      You’re welcome! You can use a code snippet from another tutorial: You can combine this code with the code from the table of contents. Just rewrite the line if($i === $middle_index) { to if($i === 1) {. And inside this if-statement you can output the table of contents.

      The table of contents will be displayed after the first paragraph in every post.

      I hope I was able to help. If you have any other questions, let me know. 🙂

      1. Deepak says:

        Thats great friend, bcz i was looking for “interlinking code” as well, but can you plz provide a detail tutorial with code either in this comment or a new article for both the tasks “interlinking” and “table of content” bcz i am one of them who knows where to put the code if someone tells but don’t understand the code fully,

        Plz create a tutorial for both tasks or provide a full code for both tasks and tell where to include in single.php and which code to include in functions.php file.

        Friend it will really help

        1. LH says:

          Hey! I have edited this post. Now you have two options to choose.
          Thanks for the idea, hope this is that what you need! 🙂

          1. Thanks for this friend, I am facing another error in linking the “table of content headings” to the headings of the article. Please create or provide a code that will automatically link the “table of content headings” with the “Article headings”. Please check any article from my site and I think you will easily understand my issue, I have implemented the code in my site.
            please solve this friend, you are the last hope in the internet world. Bcz there is no tutorial like this, which explains in deep and really solves the issue.
            thanks in advance

          2. Again Back here friend, I searched for the code to automatically insert id to the heading tags. code is here
            * Automatically add IDs to headings such as
            function auto_id_headings( $content ) {

            $content = preg_replace_callback( ‘/(\(.*)()/i’, function( $matches ) {
            if ( ! stripos( $matches[0], ‘id=’ ) ) :
            $matches[0] = $matches[1] . $matches[2] . ‘ id=”‘ . sanitize_title( $matches[3] ) . ‘”>’ . $matches[3] . $matches[4];
            return $matches[0];
            }, $content );

            return $content;

            add_filter( ‘the_content’, ‘auto_id_headings’ );
            this code works perfectly and included the ids in each heading tag of my articles, you can my site “tech behind it”, it’s still there, but linking still don’t appear. However, if I add the id from the admin panel(backend) to the headings of any article, it works, please provide a solution(code) that gives automatic linking to “table of content headings” with article headings. Thanks

          3. LH says:

            Try to put the “insert id” code before the table of contents code

          4. superb! now everything works better, i am still curious how to display h4 and h5 headings in “the table of content”.

            It’s a request of a fan of you and your site to provide code to display h4 and h5 headings in the “table of contents”.
            then it will be the most perfect open source code in the internet world

  4. LEAC says:

    Thank it works !
    I would bery much like an explanation of the regular expression used in the preg_match_all at the beginning.
    If you had time to do this, I would very much appreciate it.
    Best regards

    1. LH says:

      Good point, I will add it. 🙂

    2. LH says:

      Done 😉

  5. manish kumar says:

    Thanks Working for me

Your email address will not be published. Required fields are marked *