Markdown Render Hooks in Hugo

Posted on

Hugos’s Markdown Render Hooks allow custom templates to override Markdown rendering functionality. Render hooks are easy to setup and use and can make your site more useful and allow your Markdown content files to be simpler by passing on functionality to the render hooks rather than relying on custom code in your Markdown files.

The hook kinds currently supported are:

  • heading
  • image
  • link
  • codeblock


If your site has many subheadings then it’s useful to be able to add an anchor link to each heading so that users can easily link to a specific heading, like I have done on this site. This can be setup with a custom template for the heading hook kind.

<h{{ .Level }} id="{{ .Anchor | safeURL }}">{{ .Text | safeHTML }}
  <a class="header-link" href="#{{ .Anchor | safeURL }}"
    aria-label="Link to this section: {{ with .Text}}{{ . }}{{ end }}"></a>
</h{{ .Level }}>

This will add an anchor link to each heading that links to the heading’s anchor. The Aria label is also set to the heading’s text.

A Markdown link has three components: link text, a link destination, and optionally a link title.

Most sites have links and it’s a common practise to have external links open in a new tab. By using the Markdown render hook template below all links beginning with http will automatically have the target="_blank" attribute. You could add an external link icon within this block if you wanted.

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

One quick note: I sometimes see users add rel="noreferrer" to external links that they control, I don’t recommend that as it can make analytics data less accurate. Also, it used to be advised to add rel="noopener" to external links but the security issue for this was fixed in all major browsers years ago so this is no longer necessary.


This is perhaps the most useful render hook of all. By default, rendering an image in Markdown via Hugo will not set the size or add any classes so large images can appear wider than the viewport.

With this render hook images have their height and width added (if the images are in the static directory) and lazy loading parameters added.

<img src="{{ .Destination | safeURL }}"
  alt="{{- .Text -}}"
  {{ with .Title }} title="{{- . -}}" {{ end }}
  {{ if strings.HasPrefix .Destination "http" }} {{else}}
  {{ with imageConfig ( printf "static/%s" .Destination ) }}
    width={{ .Width }} height="{{ .Height }}"
  {{ end }}
  {{ end }}

Code Blocks

Codeblocks are the latest addition to Hugo’s render hook capabilities and were introduced with Hugo v0.93.0. Codeblocks are a little different to the other hooks as they are available as single template (render-codeblock.html) but also as templates selected based on the language of the code block (e.g. render-codeblock-html.html or render-codeblock-python.html).

1  ```html {class="html-code" id="my-codeblock" lineNos=inline tabWidth=4}
2  <body>
3    <button type="button" class="btn">Hello, world!</button>
4  </body>
5  ```

A fenced code block consists of:

  • A leading code fence
  • An optional info string
  • A code sample
  • A trailing code fence

So the code block above has an info string of html {class="html-code" id="my-codeblock" lineNos=inline tabWidth=4}. This info string is passed to the render hook template as a CodeFence object.

Some ideas for code block render hooks:

  • Add a copy button to each code block to allow your users to easily copy and paste the code.
  • Add an open in CodePen or open in StackBlitz button to allow users to quickly try out the code.
  • Add a small label of the language used in the example

Further reading

This was a quick introduction to Markdown render hooks with some examples which I hope inspire you to add to your projects.

There’s so much more you can do though - Hugo contributor Joe Mooring has written up a fantastic, in-depth guide to render hooks over on his website so I recommend you take a look.

Happy coding!

You might also like

Building a 'Share this page' feature with Hugo

privacy-focused share widgets

Migrating an RSS feed from Jekyll to Hugo

Changing the feed template and config for followers of your feed