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.

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).


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)

Then I made the file themes\<my theme>\layouts\_default\_markup\render-link.html and set it to

    href="{{ if eq .Destination `internalwiki`}}
        {{if hasPrefix .Title `http`}}
            {{ ref (.Page.Site.GetPage `/`) (.Title | lower | urlize) }}
        {{ .Destination | safeURL }}
    {{ .Text | safeHTML }}

(extra newline characters added for readability. Remove all of them to use.)


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



If everything is still working then the following links should work: