Customizing image and link output with Hugo Render hooks

Render Hooks allow custom templates to override markdown rendering functionality

* This page contains promotional content

To insert an image in Hugo you write the standard Markdown ![Text](Link-URL), which renders as <img src "~">, but I had been wanting to build native lazy-load into the img tag.
Incidentally, native lazy-load means that

You can configure lazy loading of images easily just by adding the HTML loading attribute, without needing Javascript

At first I thought it could only be inserted by using the figure tag or a shortcut and went through some trial and error, but when I looked into it I learned that it becomes possible by using Hugo’s Render hooks.
If you want to know Render hooks in detail, see the official documentation.

Building in native Lazy load

  • Add the loading=“lazy’ attribute
  • Add the decoding=“async” attribute

I built in lazy at first, but
decoding="async" and loading="lazy" do not work together even when both are written, so I went with decoding="async".
When both are written, lazy takes precedence

Hugo/layouts/_defaults/_markup/render-image.html

{{ $link := string .Destination }}
{{ if hasPrefix $link "http" }}
<img decoding="async" src="{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title}} title="{{ . }}"{{ end }} />
{{ else }}
<img decoding="async" src="{{ .Page.Permalink }}{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title}} title="{{ . }}"{{ end }} />
{{ end }}
  • Links to external URLs open in a separate tab with target="_blank”
    For security, noreferrer noopener is added as well
  • Internal links do not open a separate tab

Hugo/layouts/_defaults/_markup/render-link.html

<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener"{{ end }}>{{ .Text | safeHTML }}</a>

Gist

I have also written these customizations down in a Gist