--- title: True Backlink Support in Hugo date: 2022-04-18T08:22:00+02:00 categories: - webdesign tags: - hugo - backlinks - blogging --- A great blog engine removes friction and pushes you to write more. An average blog engine has you sweating and coming up with needless details, such as tags and other metadata. For example, on my retro gaming site [Jefklak's Codex](https://jefklakscodex.com/games/), I categorize each game entry by platform---a `platform` metadata key that had to be manually entered. After thinking things through and a thorough refactoring attempt, this is now automatically deduced based on the folder name (e.g. `/games/switch/unpacking`). One less thing to worry about. I've written before about [reducing friction and automating metadata keys](/post/2021/07/reducing-workflow-load-facilitates-writing) and how it facilitates writing. The same is true for tags, which traditionally are used in Hugo-powered blogs to find related articles. On the bottom of each Brain Baking post, the following code finds the first three related posts based on the intersection of tags, excluding the current page: ``` {{ $related := first 3 (where (where .Site.RegularPages.ByDate.Reverse ".Params.tags" "intersect" .Params.tags) "Permalink" "!=" .Permalink) }} ``` This of course only works if you dutifully---and correctly---fill in the `tags` metadata key. This works well enough for Brain Baking, but not for Jefklak's Codex, since I wanted to display similar games that link to the current one, or where the current one links to. These are called _backlinks_ and _forwardlinks_ and are very popular with [Zettelkasten](/post/2021/11/from-analog-notebook-to-digital-vault/)-like digital note tools like Obisidan and Zettlr. A backlink enables linking blog entries in a natural way instead of coming up with arbitrary tag entries---which always end up in a mess: sometimes I use `gameboy`, sometimes `Game Boy`, and sometimes `gb`. Others, such as [this method here](https://www.gabrielle-earnshaw.com/posts/implementing-backlinks-in-a-hugo-website/), have "implemented" backlinks by adding _more_ metadata to point to the correct linked article, instead of less! That's simply ridiculous and _adds_ instead of reduces friction. Yet, true backlinks in Hugo is a piece of cake thanks to the incredibly fast Go engine. The idea is very simple: - Loop through all relevant pages + If a relative link to the current page is found, it's a backlink. Collect it. + If a link on the current page to that one is found, it's a forwardlink. Collect it. - Concatenate both lists - Display the first `x` to show related articles This translates into the following Hugo shortcode: ``` {{ $currRellink := substr .RelPermalink 0 -1 }} {{ $currContent := .Content }} {{ $backlinks := slice }} {{ $forwardlinks := slice }} {{ range (where (where .Site.Pages ".Section" "in" (slice "articles" "games")) ".Params.ignore" "!=" "true") }} {{ $found := findRE $currRellink .Content 1 }} {{ if $found }} {{ $backlinks = $backlinks | append . }} {{ else }} {{ $rellink := substr .RelPermalink 0 -1 }} {{ $found = findRE $rellink $currContent 1 }} {{ if $found }} {{ $forwardlinks = $forwardlinks | append . }} {{ end }} {{ end }} {{ end }}