I was looking for a way to include HTML snippets into Hugo posts. The magic formula includes shortcode templates and readFile.

Hugo’s shortcode templates are a very neat way to bring custom, reusable pieces of HTML into your posts. It’s as simple as adding a file into the layouts/shortcodes folder. The full file structure of this example looks like this:

┌─ content
│  └─ posts
│     └─ example
│        ├─ index.md     ◄- Post where we want to include snippet.html
│        └─ snippet.html ◄- HTML snippet that we want included
├─ data
├─ layouts
│  └─ shortcodes
│     └─ include-html.html ◄- Shortcode template
└─ config.toml

Let’s start at the bottom with the shortcode:

A shortcode is a reusable HTML template that can include executable code. The name of the file is also the name of the shortcode. Our include-html.html looks like this:

{{ $file := .Get 0 }}
{{ $file | readFile | safeHTML }}

(original source is this discussion)

What happens here is that .Get 0 reads the first parameter of the shortcode and stores it in the $file variable. In the next line $file is piped into readFile which reads the content of the file. The content is then piped into safeHTML which declares the content as “safe” HTML and therefore avoids further escaping by Go templates.

In our post index.md we can now include the file snippet.html by using the include-html shortcode:

{{< include-html "content/posts/example/snippet.html" >}}

The rendered page should now include the content of snippet.html.

Having to declare the full path can be cumbersome and error prone. It would be nicer to only specify the relative path to snippet.html from the page that includes it. We can achieve that by a slight modification of include-html.html:

{{ $file := .Get 0 }}
{{ (printf "%s%s" .Page.File.Dir $file) | readFile | safeHTML }}

(printf "%s%s" .Page.File.Dir $file) concatenates .Page.File.Dir (the folder of the current page) and $file. The rest works as before. Now we can include the snippet simply like this:

{{< include-html "snippet.html" >}}

Done!

Markdown

It’s also possible to include Markdown files instead of HTML files in a very similar way. I suggest to create another shortcode named include-md:

{{ $file := .Get 0 }}
{{ $file | readFile | markdownify }}

That’s it!