Automatically add anchor links to Gutenberg headings

Ralph J. Smit Laravel Software Engineer

When you have a blog, you sometimes just want to link to the start of a specific paragraph. Especially with longer articles, I like to break it up in larger chunks. Unfortunately, the Gutenberg editor doesn't automatically add an id to every heading it generates. You'd have to do this manually. As this is a very laborious task, especially when you publish regularly, I'm happy that there is an automated way of adding this.

Below a small table of contents for this article, made possible by the automatic id's (the table is made manually):

  1. Add a filter to the_content()

  2. Wrapping up

Besides the increased user experience advantage, it also has a (slight) SEO benefit. Adding an id to every heading allows Google to link to individual paragraphs/sections on your page. The below screenshot is from the 'Pages' section in my Google Search Console and as you see, Google has linked to individual sections on my guide about cleaning up the WordPress head tag.

This also increases the user experience. By immediately offering the piece of information a user need, people are less likely to bounce back to Google, which helps to decrease your bounce rate.

Search consoles link to individual sections on a blog page by using anchor links.

A screenshot from the 'Pages' section in Google Search Console, which demonstrates that Google links to individual anchor links.

Add a filter to `the_content()`

First, we'll add a filter to the WordPress/Gutenberg post content. After the Gutenberg output is generated (e.g. a draft is saved or a change published), we'll apply this filter to automatically add the required id's.

Go to your functions.php and add the following code:

//Filter the content
function prefix_heading_ids( $content ) {
$pattern = '#(?P<full_tag><(?P<tag_name>h\d)(?P<tag_extra>[^>]*)>(?P<tag_contents>[^<]*)</h\d>)#i';
if ( preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER ) ) {
$find = array();
$replace = array();
foreach( $matches as $match ) {
if ( strlen( $match['tag_extra'] ) && false !== stripos( $match['tag_extra'], 'id=' ) ) {
$find[] = $match['full_tag'];
$id = sanitize_title( $match['tag_contents'] );
$id_attr = sprintf( ' id="%s"', $id );
$replace[] = sprintf( '<%1$s%2$s%3$s>%4$s</%1$s>', $match['tag_name'], $match['tag_extra'], $id_attr, $match['tag_contents']);
$content = str_replace( $find, $replace, $content );
return $content;
add_filter( 'the_content', 'prefix_heading_ids' );

Now you've successfully added id's to headings in Gutenberg. Every id is derived from the heading text itself. Where a heading first looked like this

<h2>Get a list of all the categories</h2>

They now look like this:

<h2 id="get-a-list-of-all-the-categories">Get a list of all the categories</h2>

Awesome isn't it?

Wrapping up

As you saw, we used the filter 'the_content' to filter and add the heading id's. This filter is called every time a page loads. This means that it may negatively impact your page performance. It's not that heavy, but it is something small to be reckoned with. Especially if you're using a lightweight theme, like I do, the benefits outweigh the downsides. Let me know if this has been helpful!

Published by Ralph J. Smit on in Guides . Last updated on 10 March 2022 .