This page looks best with JavaScript enabled

How-To add cross reference in Hugo markdown links

How to add cross reference support for Markdown links in Hugo

 ·   ·  ☕ 4 min read  · 
❄️ Yohan Yukiya Sese-Cuneta
Fediverse Follow

One of the less commonly used feature of Hugo is render hooks. In this post, we are going to use render hooks to add internal cross reference support to Markdown’s default way of creating links: [text](https://example.com#fragment "Title").

Internal cross references in Hugo is usually done by using the built-in shortcodes {{< ref >}} or {{% relref %}} but did you know since Hugo v0.62.0 1 the advisable method is to use render hooks 2?

Features

  • Use regular Markdown link format to cross reference within Hugo
    • {{< ref >}} and {{% relref %}}, or any variation thereof, is a thing of the past
    • Automatically use the content’s title
    • Multilingual support
    • Markdown link text and title support
    • With URI fragment support

What’s new

  • 2022-06-17:

    • fix: [text](./path/to/content/) and [text.ext](./path/to/file.ext) formats
  • 2022-05-27:

    • [text](./path/to/content/)
    • [text.ext](./path/to/file.ext)
    • reorganized the How To Use section

Steps

  1. Create a file called render-link.html in this directory /layouts/_default/_markup/

  2. Add the code below:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    {{- $baseurl := urls.Parse site.BaseURL -}}
    {{- $url := urls.Parse .Destination -}}
    {{- $getpage := site.GetPage .Destination -}}
    {{- $internal := lt (len $url.Host) 1 -}} {{/* NOTE: internal links will always have an empty $url.Host */}}
    
    {{- $fragment := $url.Fragment -}}
    {{- with $fragment -}}{{ $fragment = printf "#%s" $fragment }}{{- end -}}
    
    {{- $destination := "" -}}
    {{- if $internal -}}
      {{- if (strings.HasPrefix $url.Path "./") -}}
        {{/* NOTE: for links starting with ./ */}}
        {{- $urltrimmed := strings.TrimPrefix "./" $url -}}
        {{- $destination = printf "%s://%s/%s%s" $baseurl.Scheme $baseurl.Host $urltrimmed $fragment -}}
      {{- else -}}
        {{/* NOTE: for internal links */}}
        {{- $destination = printf "%s%s" $getpage.RelPermalink $fragment -}}
      {{- end -}}
    {{- else -}}
      {{- $destination = .Destination -}}
    {{- end -}}
    
    <a href="{{ $destination | safeURL }}"{{ with or .Title $getpage.LinkTitle .Text }} title="{{ . }}"{{ end }}{{ if not $internal }} rel="noopener external"{{ end }}>{{ or .Text .Title $getpage.LinkTitle | safeHTML }}</a>
    

That’s it.

How to use

Traditional

1
2
3
4
- [https://example.com/#fragment](https://example.com/#fragment "https://example.com/#fragment")
- [direct](https://im.youronly.one/techmagus/browser-wars-3-blink-gecko-2018344/ "Title")
- [direct with #fragment](https://im.youronly.one/techmagus/browser-wars-3-blink-gecko-2018344/#fragment "Title")
- [#fragment only](#fragment)

New

Special

The [text](./path/to/content/) format is useful when you want to create a link to another part of your website, under the same (sub)-domain but not part of the current Hugo project. This format will not generate an external link icon if the How-To add link icons in Hugo markdown links is also installed.

The [text.ext](./path/to/file.ext) is useful for download links hosted under the same (sub)-domain, within or external relative to the current Hugo project.

1
2
- [send a gift](./p/gift/)
- [link-icons.7z](./techmagus/dls/link-icons.7z)

Without a file extension

1
2
3
4
5
6
7
8
- [](20181210-browser-wars-iii)
- [](/20181210-browser-wars-iii)
- [](20181210-browser-wars-iii "Title")
- [](/20181210-browser-wars-iii "Title")
- [Text](20181210-browser-wars-iii)
- [Text](/20181210-browser-wars-iii)
- [Text](20181210-browser-wars-iii "Title")
- [Text](/20181210-browser-wars-iii "Title")

With file extension

1
2
3
4
5
6
7
8
- [](20181210-browser-wars-iii.md#fragment)
- [](/20181210-browser-wars-iii.md#fragment)
- [](20181210-browser-wars-iii.md#fragment "Title")
- [](/20181210-browser-wars-iii.md#fragment "Title")
- [Text#fragment](20181210-browser-wars-iii.md#fragment)
- [Text#fragment](/20181210-browser-wars-iii.md#fragment)
- [Text#fragment](20181210-browser-wars-iii.md#fragment "Title")
- [Text#fragment](/20181210-browser-wars-iii.md#fragment "Title")

Not supported

Internal linking without a file extension and with a #fragment produces a wrong link.

1
2
3
4
5
6
7
8
- [](20181210-browser-wars-iii#fragment)
- [](/20181210-browser-wars-iii#fragment)
- [](20181210-browser-wars-iii#fragment "Title")
- [](/20181210-browser-wars-iii#fragment "Title")
- [Text#fragment](20181210-browser-wars-iii#fragment)
- [Text#fragment](/20181210-browser-wars-iii#fragment)
- [Text#fragment](20181210-browser-wars-iii#fragment "Title")
- [Text#fragment](/20181210-browser-wars-iii#fragment "Title")

Also take note of the following formats.

1
2
- [link-icons.7z](/dls/link-icons.7z)
- [link-icons.7z](../../dls/link-icons.7z)

Instead of the above, use [text](./path/to/file.ext) like so [link-icons.7z](./techmagus/dls/link-icons.7z) will render as: link-icons.7z

Improve it

Feel free to improve the code above and share back to the community. Please provide your website and name so you will be properly credited.


Did you like it? Do share this post, leave a comment below, and send me a gift! (opens in a new tab/window)