Related Posts in Hugo

Personally, when I get to an article that I enjoy on someone’s blog, I’ll often look to read other articles that are similar. This can usually be accomplished by clicking on a category or a tag. Ideally, there is a list of related posts near the end of the article, as this makes finding related content that more accessible. This sort of internal linking is also really important to search engine optimization for your own personal site.

But how does one do this in Hugo? Can you actually make a related content section in a static site? I recently went through this process myself, and found it to be relatively straight forward.

Websites, or more specifically blogs, are generally organized into a series of posts which all fall within one of a handful of categories. Often, these categories can be clicked on and you are taken to a set of posts with similar themes. For example, this post is categorized in Web Development. You can see this at the top of the post. Clicking on it will take you to a page listing all content in this category. Webpages which have a defined set of themes will often have many posts within each category.

So, are categories just a simple way to find related content? Absolutely. However, as you can see at the top of this post, I’ve also assigned it a set of tags. Tags should, from my understanding, provide smaller sets of posts which have to do with specific sub categories. For example, this post is tagged as hugo, SEO, and blogging. In theory, each of these lists will return a subset of Web Development posts that are more specific to a topic. Each tag can be considered to provide the reader with related content.

Research has shown that search engines give higher ratings to posts which have strong linking. This usually refers to large numbers of external links (called backlinks because they link back to your site), but it’s also been shown to be beneficial to have strong internal linking within your site1. These internal links help search engines understand your site structure better, and provide context for your articles2. This sort of context will hopefully allow search engines to serve content that is interesting to the perspective reader.

In summary, related posts not only provide benefits to your readers, but they provide search engine ranking benefit to your site. Given that, there seems no reason not to add related posts to your site.

Now that we’ve defined ways of finding related content, wouldn’t it be even more powerful if you could define an algorithm that chose posts based on tags and categories (and even publish date)? This would work from most specific (tags) to less specific (categories) to provide your readers with something else to read that is similar and they might find interesting.

This will depend highly on how your site is organized and how your content is related. For example, for this site I’m really not interested in posts being in close time proximity. Things such as photos and photography skills aren’t significantly time dependent, and therefor older content with similar photos would still interest readers. Someone viewing nature images would be most likely to look for other nature images, or other nature content in a different category even.

Thankfully, this is easily possible with built-in the functionality of Hugo.

The Hugo documentation discussing Related content lays out what appears to be a relatively simple concept. There are multiple different methods available to choose what content you’d like listed.

In addition to listing related content, Hugo also allows for you to configure what parameters you’d like to consider to call consider something related.

For my site, I did something simple and defined a Hugo partial template named related.html. Partials are essentially a reusable piece of HTML for your site. This allows me to add the partial to multiple layouts such as my photo posts or my regular blog posts.

{{ $related := .Site.RegularPages.Related . | first 5 }}
{{ with $related }}
<div class="related-section">
  <h3>
    Related Posts
  </h3>
  <ul>
      {{ range . }}
      <li><a href="{{ .RelPermalink }}">{{ .Title }} </a></li>
      {{ end }}
  </ul>
</div>
{{ end }}

As you can see from the above code, I chose to use the .Site.RegularPages.Related function because RegularPages eliminates the taxonomy pages (tags, categories, and index pages for example). The rest of the code simply adds a header, and creates an unordered list of the related content.

Now this is where I get rather excited. Hugo goes above and beyond just listing related posts, it allows you to configure what you want considered, and even assign weights.

Looking at the documentation, you can see that adding some configuration into your config.toml file is all it takes. Here’s my configuration, based on how I believe the content on this site is consumed:

[related]
  includeNewer = true
  threshold = 85
  toLower = false

  [[related.indices]]
  name = "tags"
  weight = 80

  [[related.indices]]
  name = "keywords"
  weight = 40

  [[related.indices]]
  name = "date"
  weight = 10

I’ll explain my rationale for my settings, but again, you’ll probably need to modify this for your own site.

includeNewer - The documentation warns that this may modify existing static pages as you add newer content. Personally, I find this to be an essential feature for me, and I have my site configured to be fully rebuilt (via GitLab pipelines) every day, so I’m happy to enable this.

threshold - Basically, this seems to be how similar pages need to be before they are considered related. I started this out at 70 and found that pages were being included that weren’t close enough for my liking. So I began bumping this up until I hit a value that appeared to provide me the results I was looking for.

toLower - Per the documentation, this should convert everything to lowercase prior to comparing, which should provide slightly more accurate results at a performance penalty. I tried this both ways, and noticed no real difference in results, so I left it disabled.

Now things get really interesting. Hugo allows you to specify what indices you want considered for determining similar posts. As I mentioned earlier, I’m mainly interested in finding posts that have similar tags, mainly because I’ve been really careful wih my use of tags. Therefore, I’ve chosen to utilize tags as my first indices, and give it a weight of 80.

Secondly, I’m interested in keyword matching. I started out with a much higher value here, but was finding it was way too sensitive for my liking and I was getting posts that were completely unrelated in content, yet utilized similar language. I continued to lower this weight until I settled on a value of 40, which appears to work well for my site.

Finally, I’ve configured date as an indices, but gave it only a weight of 10. Essentially, what I’ve found this to do is when I have more than 5 related posts, I’d like those that are of a similar age.