Wiki Links in Hugo
Since I started using Hugo for this site there’s been one major missing feature for me - wiki style links. In this article I will describe a hack to add the syntax as part of a custom theme.
- Wiki links are links that look like
[[page|label]]
with|label
optional. Generally thepage
input is the title of the page, but it could also be a url. - Hugo is my static site generator of choice. Find more info on hugo’s website
Unfortunately, the maintainer of Hugo has declared that Hugo will not be adding support for the wiki-style link syntax used by Obsidian, MediaWiki, and many other applications. This is especially frustrating because earlier responses to the open ticket suggested that support would be possible eventually. I had pushed to get progress made on this, but that push resulted in the ticket being closed. You can see more of my discussion here as well.
Lucky for me, jmooring dropped some hints about how something like this might be hacked into a theme. With that information I was able to find a way to add exactly what I wanted. This as opposed to the original solution that was offered ([[page|label]]()
syntax or [label](page)
syntax where label is required).
The ultimate goal here is for me to be able to transfer markdown files back and forth between Hugo and Obsidian. So I can’t compromise on the syntax. I also want to avoid having to type the title of a page twice if I’m writing a link to it and I want the link label to be the title (on my site, all of my page urls contain the page titles).
Implementation
It turns out that I can do this in exactly two steps:
Replace .Content
Wherever I was using .Content
in my theme I replaced that with
{{ .RenderString
(replaceRE `\\(\[[[^]]*]])` `$1` (
replaceRE `(^|[^\\]{1})(?:(\[\[([^]||]*)]])|(\[\[([^]||]*)\|([^]]*)]]))` `$1[$3$6](internalwiki "$3$5")` .RawContent)
)
}}
(extra newline characters added for readability)
Create a Link Render Hook
Then I made the file themes\<my theme>\layouts\_default\_markup\render-link.html
and set it to
<a
href="{{ if eq .Destination `internalwiki`}}
{{if hasPrefix .Title `http`}}
{{.Title}}
{{else}}
{{ ref (.Page.Site.GetPage `/`) (.Title | lower | urlize) }}
{{end}}
{{else}}
{{ .Destination | safeURL }}
{{end}}">
{{ .Text | safeHTML }}
</a>
(extra newline characters added for readability. Remove all of them to use.)
Explanation
The first part of this uses regex find and replace to take items of the form
[[My Link]]
and transforms them into
[My Link](internalwiki "My Link")
and items of the form
[[My Link|My Label]]
and transforms them into
[My Link](internalwiki "My Label")
effectively taking the wiki style link and turning it into a standard markdown link of the form [Text](Destination "Title")
but with the destination set to “internalwiki” and the label, if present, temporarily stored in the “title” syntax.
With that in place, links are then picked up by the new render hook. The new render hook renders links normally except when the destination is set to “internalwiki”. In that case it replaces the href for those links with a ref lookup (Hugo search for page) of the lowercased and urlized Title value (which we moved into place in the first part). And the title property, if present set to replace the link text. If you don’t want your urls to be dash separated and all lowercase (for example Obsidian style file names) you may want to adjust the way that Title is processed.
Special Cases
- Prefixing the
[[]]
syntax with a\
will only remove the\
instead of running the link behavior. This is mostly so that I could write this article.
Assumptions
- Pages in Hugo with the title “Title of my Page” have file names of the form title-of-my-page.md
- External wiki style links use the http protocol
- Two wiki links can never be immediately next to each other (the regex check for the
\
escape syntax causes this to fail)- There’s probably a good way to fix this but I’ve spent more time on this than I intended already.
Samples
If everything is still working then the following links should work:
[[Know Your System]]
: Know Your System[[3 Qualities of Good Documentation|Good Documentation]]
: Good Documentation[[https://www.example.com/| This is an example]]
: This is an example