How to Add Anchor Links to Headers in Hugo


The great thing about adding anchor links to headers in Hugo is that Hugo already adds an id to the headers on every page. Our job is simply to link to that id.

First, we need to know where our content is being displayed.

We’ll do that by searching for usage of .Content.

Normally, we’ll find it in single.html.

{{ .Content }}

1. Using Hugo templating

We can replace the above {{ .Content }} with the following:

{{- with .Content -}}
  {{ . | replaceRE "(<h[2-4] id=\"([^\"]+)\".+)(</h[2-4]+>)" `${1}<a href="#${2}" class="hanchor" ariaLabel="Anchor">#</a>${3}`| safeHTML }}
{{- end -}}

The regex simply groups header tag elements (e.g. <h2>, <h3>, <h4>) and replaces it with the same elements including an anchor tag at the end of the it.

We’ll style the hanchor class below.

We can target specific header tags by modifying h[2-4] to be h2 or h[3-5] or whatever we want.

Similarly, we can modify the actual anchor text. Lots of people prefer the link icon 🔗 over the hash symbol #.

2. Using JavaScript

We can also use JavaScript to create the anchor link.

After the DOM has been loaded, we want to target all headers tags in our content.

In my Hugo site, my {{ .Content }} is wrapped in a div with class=article-post, so I’ll specifically target headers within that div (e.g. .article-post h2[id]). Be sure to change this depending on your Hugo layout.

document.addEventListener("DOMContentLoaded", function (event) {
  const headers = document.querySelectorAll(
    '.article-post h2[id], .article-post h3[id], .article-post h4[id]')
  if (headers) headers.forEach(addAnchor)
});
function addAnchor(element) {
  element.insertAdjacentHTML(
    'beforeend', 
    `<a href="#${element.id}" class="hanchor" ariaLabel="Anchor">#</a>`
  );
}

Styling the anchor

Here are my recommended styles for quality anchor text.

.hanchor {
  visibility: hidden;
  color: silver;
  font-size: 100%;
  transition: 0.2s;
  padding-left: 8px;
  font-weight: 600;
}
h2:hover a, h3:hover a, h4:hover a {
  visibility: visible;
  text-decoration: none;
}