Create Automatic Table of Content WordPress no Plugin

Create Automatic Table of Content WordPress no Plugin
Create Automatic Table of Content WordPress no Plugin

Exhemes Dev Blogtable of contents for WordPress posts and articles is a clever thing. Visitors have a quick overview, SEO benefits and chic design really enhance your post!

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 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.

Table Of Contents

Table of Contents no Plugin Codes

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.

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

// declare a function and pass the $content as an argument
function insert_table_of_contents($content) {

	// used to determine the location of the
	// table of contents when $fixed_location is set to false
	$html_comment = "<!--insert-toc-->";
	// checks if $html_comment exists in $content
	$comment_found = strpos($content, $html_comment) ? true : false;
	// set to true to insert the table of contents in a fixed location
	// set to false to replace $html_comment with $table_of_contents
	$fixed_location = true;

	// return the $content if
	// $comment_found and $fixed_location are false
	if (!$fixed_location && !$comment_found) {
		return $content;

	// exclude the table of contents from all pages
	// other exclusion options include:
	// in_category($id)
	// has_term($term_name)
	// is_single($array)
	// is_author($id)
    if (is_page()) {
        return $content;
	// regex to match all HTML heading elements 2-6
	$regex = "~(<h([2-6]))(.*?>(.*)<\/h[2-6]>)~";

	// preg_match_all() searches the $content using $regex patterns and
	// returns the results to $heading_results[]
	// $heading_results[0][] contains all matches in full
	// $heading_results[1][] contains '<h2-6'
	// $heading_results[2][] contains '2-6'
	// $heading_results[3][] contains '>heading title</h2-6>
	// $heading_results[4][] contains the title text
	preg_match_all($regex, $content, $heading_results);

	// return $content if less than 3 heading exist in the $content
	$num_match = count($heading_results[0]);
	if($num_match < 3) {
		return $content;

	// declare local variable
	$link_list = "";
	// loop through $heading_results
	for ($i = 0; $i < $num_match; ++ $i) {

	    // rebuild heading elements to have anchors
	    $new_heading = $heading_results[1][$i] . " id='$i' " . $heading_results[3][$i];

	    // find original heading elements that don't have anchors
	    $old_heading = $heading_results[0][$i];

	    // search the $content for $old_heading and replace with $new_heading
		$content = str_replace($old_heading, $new_heading, $content);

	    // generate links for each heading element
	    // each link points to an anchor
	    $link_list .= "<li class='heading-level-" . $heading_results[2][$i] .
	    	"'><a href='#$i'>" . $heading_results[4][$i] . "</a></li>";
	// opening nav tag
	$start_nav = "<nav class='table-of-content'>";
	// closing nav tag
	$end_nav = "</nav>";
	// title
	$title = "<h2>Table of Contents</h2>";

	// wrap links in '<ul>' element
	$link_list = "<ul>" . $link_list . "</ul>";

	// piece together the table of contents
	$table_of_contents = $start_nav . $title . $link_list . $end_nav;

	// if $fixed_location is true and
	// $comment_found is false
	// insert the table of contents at a fixed location
	if($fixed_location && !$comment_found) {
		// location of first paragraph
		$first_paragraph = strpos($content, '</p>', 0) + 4;
		// location of second paragraph
		$second_paragraph = strpos($content, '</p>', $first_p_pos);
		// insert $table_of_contents after $second_paragraph
	return substr_replace($content, $table_of_contents, $second_paragraph + 4 , 0);
	// if $fixed_location is false and
	// $comment_found is true
	else {
		// replace $html_comment with the $table_of_contents
		return str_replace($html_comment, $table_of_contents, $content);
// pass the function to the content add_filter hook
add_filter('the_content', 'insert_table_of_contents');


Css Style for table of contents

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.

I have created two different styles. Just use the code from the table of contents that you like best.

// headings tags below level-2 are pushed to the right by 40px
// you can also choose to style each heading element differently
 .heading-level-6 {
    margin-left: 40px;


Congratulations on making it through this tutorial. Our function is now complete. Don’t forget to style the table of contents to your liking. Be creative!


Howdy, I’m exthemes. I’m a web developer living in indonesia. I am a fan of web development, seo, and php. I’m also interested in wordpress and web designer.

You May Like These Posts


  1. 4 months ago

    nice trick..