Parsing Backlinks in Hugo
— Kaushal ModiA Hugo partial to parse all the backlinks to any post from the same Hugo-generated website.
If a post is referred to in other posts, then all those posts are creating backlinks to that first post.
As of writing this (# 8077) to track this feature request.
), Hugo doesn’t have a built-in way to generate a list of such backlinks, though there’s an open issue (One way to gather a list of backlinks to a post is to find out that post’s relative or absolute permalink, and search for the occurrences of that link in all the other posts on the published site. The author of seds.nl: Export org-roam backlinks with Gohugo, Ben Mezger, uses this approach in his solution for creating backlinks in that post.
In this post, I am expanding upon that solution and refactoring it bit to fit my needs.
Code #
Without further ado, here is my version of the partial:
1 Create a backlinks.html
partial #
Save this partial to your site repo as
layouts/partials/backlinks.html
.
|
|
2 Use the partial #
Add a call to this partial in your “single” layout’s template file,
which is typically layouts/_default/single.html
.
{{ partial "backlinks.html" (dict "page" .) }}
Features and Improvements #
- ✨ The partial now accepts a
dict
or a dictionary with keyspage
andheading
.- The
page
key is required to pass the page context from where the partial is called. - The
heading
key is optional. This can be used by the user to set the “Backlinks” heading differently. For example,{{ partial "backlinks.html" (dict "page" . "heading" "<h4>Links to this note</h4>") }}
.
- The
- 🐛 Line 2:
.File.BaseFileName
would be just “index” for all the Leaf Bundles If you follow this link and scroll to the bottom of that page, you will see a “Backlinks” section auto-generated with the help of this partial. This post will be linked there because I just referenced that post here. , and I use them heavily! Using.File.ContentBaseName
fixes this problem. See 📖 Hugo File Variables for more info. [Credit: @sjgknight] - 🐛 Line 3: Reduce false matches for
backlinks by making the regular expression a bit stricter. Now it
will match only if the derived
$path_base
variable is found wrapped in characters like"
,/
,(
or)
.- If
$path_base
is ‘hello’, I don’t want its mere reference like in this line to create a backlink on that ‘hello’ post! - Instead, a match will happen only if something like
"hello"
(as in{{< relref "hello" >}}
), or/hello"
(as in{{< relref "/posts/hello" >}}
), or/hello)
(as in[Hello](/hello)
) is found in the raw Markdown content (.RawContent
).
- If
- ⚡ Line 5: Look for backlinks only in
site.RegularPages
. See 📖 About site Pages variables for more info.site.AllPages
includes all pages.. even the list pages like section and taxonomy pages which, I believe, won’t contain backlinks. - ⚡ The
findRE
in line 6 is slightly optimized by quitting the search immediately as soon as the first match is found. - 💄 Rest of the changes are just using a different style of coding using Hugo templates and creating a different structure in HTML.
Closing #
This partial works great for this site — adds only a few hundred milliseconds to the build time.
But it’s not an efficient solution. The partial is called in the single template where it searching for backlinks to the current page in all other regular pages, and the single template is evaluated for all the regular pages. So its \(O\) notation will be close to \(O(n^2)\) where \(n\) is the number of regular pages.
I have only about a hundred regular pages at the moment, but I can see
this partial taking a major chunk of the build time
The hugo --templateMetrics --templateMetricsHints
command prints a
table listing all the partials used in the build and how much time
each of them took. See 📖 Hugo Build Performance for more
info.
as the number of pages increase.
A built-in support for backlinks from Hugo (# 8077) would really help in this performance department.