Since writing the how-to article on using nextgen formats for images in Hugo, I’ve been investigating Hugo’s markdown render hooks and found that they are much simpler to implement. There is less of a faff passing lots of parameters into a shortcode. In this tutorial, I will show you how to get the same result using much less code. In addition, this article will show you a simple way to achieve _target=blank in .md files for links without using javascript.

What are Markdown Render Hooks

Render hooks enable you to change the rendering of markdown elements such as images or links. In Hugo, we can create render hooks for

  • images
  • links
  • codeblocks
  • headings

You write templates that allow you to change the rendering from the standard format to one that matches your custom requirements.

To create a render hook template, you create a HTML file where the filename corresponds to the singular version of the names stated above hence:

  • images become render-image.html
  • links become render-link.html
  • codeblocks becomes render-codeblock.html
  • headings become render-heading.html

These HTML files will hold the code that transforms your content into the rendered HTML.

From Shortcodes to Render Hooks

In this tutorial, I will focus on images and links. You may remember in the previous tutorial, I used the figure shortcode to render images in my markdown, which looked like this.

1
{{< figure src=foo.jpg caption="this is a caption" >}}

Using the above, we can add things like classes or alt text, but you may also remember there was a slew of potential parameters passed into the shortcode to render the height and width, extra style, links and more.

Instead of using the above template, we will use a simpler one that takes the regular format for an image in markdown, which looks like this:

1
![this is a caption](foo.jpg)

or if the image has a link, one that looks like this.

1
[![this is a caption](foo.jpg)](/link-to-image)

and renders it using the same image resource processing that we built in the previous article.

Render Hook Templates

Render hook templates live in the layouts/_default/_markup folder either in your theme or the top-level layouts folder. You can also create type or section-specific render hook templates, as you would for any list or single templates, if you need images or links rendered differently for alternate content types/sections.

Since I have the same format for images in all the sections of my site, I will add my template to the _default folder.

Both the image and link render hooks get the following context passed to them.

  • Page - the page that is being rendered
  • Destination - the URL of the image/link
  • Title - any title attribute
  • Text - the rendered HTML link text
  • PlainText - the plain text equivalent of the Text

Image Render Hook Template

Do you remember the Figure shortcode from the previous article

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// layouts/shortcodes/figure.html
{{ $src := .Get "src" }}
{{ $class := .Get "class" }}
{{ $link := .Get "link" }}
{{ $target := .Get "target" }}
{{ $rel := .Get "rel" }}
{{ $alt := .Get "alt" }}
{{ $caption := .Get "caption" }}
{{ $width := .Get "width" }}
{{ $height := .Get "height" }}
{{ $style := .Get "style" }}
{{ $attr := .Get "attr" }}
{{ $attrlink := .Get "attrlink" }}
{{ $title := .Get "title" }}
{{ $lazyload := .Get "lazyload" }}

<figure {{ with $class }} class="{{ . }}" {{ end }}>
    {{ with $link }}
    <a href="{{ . }}" {{ with $target }} target="{{ . }}" {{ end }}{{ with $rel }} rel="{{ . }}" {{ end }}>
        {{- end -}}
        {{ with .Page.Resources.GetMatch $src }}
        {{ partial "_images/picture" (dict "src" . "class" $class "alt" $alt "lazy" "true") }}

        {{ end }}
        {{- if $link }}</a>{{ end -}}
    {{- if or (or ($title) ($caption)) ($attr) -}}
    <figcaption>
        {{ with $title -}}
        <h4>{{ . }}</h4>
        {{- end -}}
        {{- if or ($caption) ($attr) -}}
        <p style="text-transform: uppercase; font-size: x-small;">
            {{- $caption | markdownify -}}
            {{- with $attrlink }}
            <a href="{{ . }}">
                {{- end -}}
                {{- $attr | markdownify -}}
                {{- if $attrlink }}</a>{{ end }}
        </p>
        {{- end }}
    </figcaption>
    {{- end }}
</figure>

This code can all be replaced by

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// layouts/_default/_markup/render-image.html
{{ $alt := .Text }}
{{ $title := .Title | default $alt }}
{{ with .Page.Resources.GetMatch .Destination }}
<figure>
  {{ partial "_images/picture" (dict "src" . "alt" $alt "title" $title "lazy" "true") }}
  {{ with $alt }}
  <figcaption>
    <p style="text-transform: uppercase; font-size: x-small;">{{- $alt -}}</p>
  </figcaption>
  {{ end }}
</figure>
{{ end }}

We are still doing the same thing, getting the same result but with much less code. Also, I don’t have to remember to add a ‘figure’ shortcode statement in the file; I can use regular markdown.

So what’s the code here doing:

  • Ln 2-4: We assign two parameters to local variables to work with them in the _images/picture partial. I want all images to have a caption for the most part, so I’ve opted to add the alt text to the title element if no title has passed.
  • Ln 4: We use a with statement so that Hugo will not error if it can’t find the image.
  • Ln 5: Because I want my images to have a figcaption, I wrap it in a figure element
  • Ln 6: In the previous tutorial, we spoke about the _images/picture partial. It’s the one that does all the image processing
  • Ln 7: We use another with statement so that the figcaption is only rendered if we have .Text passed in.

And that’s it!

I wanted links to external websites to open using _target=blank. If you do a Google search for Hugo link target blank, the resulting pages mention using javascript a lot.

You don’t need JavaScript to achieve this if you want links to external websites to open in a new window or tab. You create your own render-link.html file and add the following.

1
2
3
4
5
6
7
// layouts/_default/_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>

Ln 4 is where the magic happens. Add a check to see if the link begins with http, and that will add the _target=blank attribute to your link. Otherwise, it will open the link in the same browser window. Simples!

Conclusion

Using Hugo’s render hook templates, we can write less code and render images how we want them to appear. Using these hooks, we can achieve a standardised way of generating pictures and links consistently on our site.

If you liked this post, please share it with others: