Emacs, scripting and anything text oriented.

titleref: Referencing Hugo posts by their titles

Kaushal Modi

A custom Hugo shortcode to create cross-references to other posts using their title strings.

relref shortcode #

Hugo has a built-in shortcode relref that gets relative links  There’s also a ref shortcode which creates absolute links. ref can be used wherever relref is used. to other posts by using their url. In Hugo lingo, a post’s url consists of the post’s section and slug  This post’s section is posts and slug is titleref-referencing-hugo-posts-by-their-titles. So the url is posts/titleref-referencing-hugo-posts-by-their-titles. .

This page’s relative link can be fetched by using {{< relref "posts/titleref-referencing-hugo-posts-by-their-titles" >}}. If a post’s slug is unique across all the posts, its section part can be skipped. So the same relref can be written as {{< relref "titleref-referencing-hugo-posts-by-their-titles" >}} and that will still return this same link.

Problem Statement
The issue with relref is that it returns only the link — the description still needs to be written manually. I typically use a post’s title as description when I link to it. So the full link with description will look like this when writing in Org mode  In Markdown, the same link will look like this: [titleref: Referencing Hugo posts by their titles]({{< relref "titleref-referencing-hugo-posts-by-their-titles" >}}). for exporting using ox-hugo: [[@@hugo:{{< relref "titleref-referencing-hugo-posts-by-their-titles" >}}@@][titleref: Referencing Hugo posts by their titles]].

You can see how impractical and verbose that is!

If you write your posts in Markdown, you can jump to the titleref shortcode section.

relref Org macro #

An Org macro can help a little over here.

#+macro: relref @@hugo:[@@ $1 @@hugo:]({{< relref "$2" >}})@@
Code Snippet 1: relref Org macro

With this macro, that same relref link can be rewritten as {{{relref(titleref: Referencing Hugo posts by their titles,titleref-referencing-hugo-posts-by-their-titles)}}}.

It’s a bit less verbose compared to using the shortcode directly, but I still wasn’t happy with the redundancy.

titleref shortcode #

In that relref link, we are providing both, the post slug and the post title. Both are uniquely associated with each-other , and Hugo can internally derive one from the other. So there’s no need for us to specify both!

And so I thought that it should be pretty simple to specify just the title of post that I want to link, and let Hugo derive the post’s permalink — and it was simple, by using this custom  A user can add custom shortcodes or override the default Hugo shortcodes by putting them in the layouts/shortcodes/ directory of the site or theme repo. shortcode!

An astute Org mode user might think that this idea looks like the [[*Heading]] syntax supported by Org, and they would be right! This titleref idea is inspired by that Org mode feature.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{{- $title_anchor := (.Get 0) -}}
{{- $title := trim (index (split $title_anchor "#") 0) " " -}}
{{- $anchor := trim (index (split $title_anchor "#") 1) " " -}}
{{- $desc := (.Get 1) | markdownify -}}
{{- $filtered := where site.RegularPages "Title" $title -}}
{{- $first_match := index $filtered 0 -}}
{{- with $first_match -}}
    {{- if $anchor -}}
        {{- $anchor = printf `#%s` $anchor -}}
    {{- end -}}
    {{- $anchor = $anchor | default "" -}}
    {{- $desc = $desc | default .Title -}}
    {{- (printf `<a href="%s%s">%s</a>` .RelPermalink $anchor $desc) | safeHTML -}}
{{- else -}}
    {{- (errorf `%s: Page titled "%s" not found` .Position $title) -}}
{{- end -}}
Code Snippet 2: titleref Shortcode

This shortcode is designed to take up to two arguments:

title (+anchor)
The first argument can be just the title of the post that we want to link (e.g. “My post”). Optionally, it can also be followed by an anchor in that post, separated by “#” (e.g. “My post#some-heading”).
(description)
The second argument is optional, and it’s the link description. If this argument is not set, the description is set to the post’s title.
How this shortcode works
  • In lines 2 and 3, the title and anchor portions are parsed from the first argument.
  • In line 4, if the description is specified using a second argument, it’s converted to HTML using markdownify.
  • Line 5 is the main logic – It returned a list of all the pages whose Title matches the title parsed from the first argument. This list should have only 1 element as the assumption is that all blog posts have unique titles.
  • The first (and only) page element of that list is saved on $first_match in line 6.
  • If the Title based search was successfully in that main logic, $first_match won’t be nil. In line 12, the link description is set to the .Title from that page object, if a description wasn’t already set using the second argument. Finally an HTML link is printed by using .RelPermalink (relative permalink) from the page object and the description.
  • If the Title based search fails in the main logic, an error is printed on line 15.
Examples of using the titleref shortcode (for Markdown users)
  • {{< titleref "TITLE" >}} – Link to a page with that TITLE and set the link description to be the same as the TITLE.
  • {{< titleref "TITLE" "DESC" >}} – Link to a page with that TITLE, with DESC as description.
  • {{< titleref "TITLE#ANCHOR" "DESC" >}} – Link to an ANCHOR in a page with that TITLE, with DESC as description.

titleref Org macro #

But the fun doesn’t end here for an Org mode user! 😃

I do not prefer using Hugo shortcodes directly in my Org source. So I created an Org macro to wrap this shortcode.

#+macro: titleref @@hugo:{{< titleref "$1" "@@ $2 @@hugo:" >}}@@
Code Snippet 3: titleref Org macro

Now {{{titleref(titleref: Referencing Hugo posts by their titles,)}}} will export to titleref: Referencing Hugo posts by their titles.

Here are some example uses of this macro:

  • {{{titleref(TITLE,)}}} – Link to a page with that TITLE and set the link description to be the same as the TITLE.
  • {{{titleref(TITLE,DESC)}}} – Link to a page with that TITLE, with DESC as description.
  • {{{titleref(TITLE#ANCHOR,DESC)}}} – Link to an ANCHOR in a page with that TITLE, with DESC as description.

Summary #

By using the titleref shortcode (or Org macro) instead of relref, we now have a tremendous reduction of redundancy.

Before
  • Org mode: {{{relref(titleref: Referencing Hugo posts by their titles,titleref-referencing-hugo-posts-by-their-titles)}}}
  • Markdown: [titleref: Referencing Hugo posts by their titles]({{< relref "titleref-referencing-hugo-posts-by-their-titles" >}})
Now
  • Org mode: {{{titleref(titleref: Referencing Hugo posts by their titles,)}}}
  • Markdown: {{< titleref "titleref: Referencing Hugo posts by their titles" >}}

Versions used: hugo 0.93.0
This is Day 7 of #100DaysToOffload.
Webmentions
Comment by Anonymous on Thu Mar 3, 2022 11:01 EST

I am pleasantly surprised someone read through the RSS feed 😄

Well… we are watching you… Still pleasantly surprised? :D

Comment by Kaushal Modi on Tue Mar 1, 2022 12:39 EST

Your RSS feed has a fancy .. email address

I already have my actual email in the RSS webMaster tags, but I don’t have an email associated with each post, so I just made that up. My thinking is that if it’s easy to pass a validity test, why not do it?

I am pleasantly surprised someone read through the RSS feed 😄

Comment by Anonymous on Tue Mar 1, 2022 11:24 EST
I am only commenting, because I wanted to see, how you do this ;) Anyway… Your RSS feed has a fancy Kaushal.Modi@fakeEmailToMakeValidatorHappy.com email address… did you ever think of using a throwaway email address or just ignoring what a validator tells you is right? I don’t think an email is required for a proper RSS feed.