Hugo Component Pattern
3 minute readA clean and simple approach I use to create repeating elements on Hugo sites.
For this example we’ll make a block to display user testimonials.
The testimonials might be shown in more than one place on the site, so it’s important that the data be stored in a single place (otherwise when testimonials change in future we’ll need to change it everywhere manually, risking missing some instances).
Create data file: /data/testimonials.toml
For this project the testimonials require a name, location and a little bit of text. To keep it super simple the name and location will be displayed in a single element, we’ll use just 2 fields, heading
and body
.
Saving the TOML to a dedicated testimonials.toml
file in the /data
directory makes it easy to get at from a partial.
# /data/testimonials.toml
[[testimonials]]
heading = "Alan Best, Brighton"
body = "A professional, trustworthy service."
[[testimonials]]
heading = "Christine Danes, London"
body = "Couldn't ask for more!"
[[testimonials]]
heading = "Elliot Fox, Bristol"
body = "I couldn't be happier!"
TOML syntax is weird with the repeating [[testimonials]]
. For comparison, this is the same data as JSON:
"testimonials": [
{
"heading": "Alan Best, Brighton",
"body": "A professional, trustworthy service."
}, {
"heading": "Christine Danes, London",
"body": "Couldn't ask for more!"
}, {
"heading": "Elliot Fox, Bristol",
"body": "I couldn't be happier!"
}
]
Create partial: /layouts/partials/testimonials.html
The /data/testimonials.toml
file we just created automatically becomes accessible in code as .Site.Data.testimonials
.
We want to output the data as an HTML unordered list (<ul>
). So we loop through the data, creating a list item (<li>
) for each testimonial.
<!-- /layouts/partials/testimonials.html -->
<ul>
{{- range .Site.Data.testimonials -}}
{{- range . -}}
<li>
<h3>{{- index . "heading" -}}</h3>
<p>{{- index . "body" -}}</p>
</li>
{{- end }}
{{- end }}
</ul>
To explain the two nested range
blocks:
- The outer
{{- range .Site.Data.testimonials -}}
loops through the 3 testimonials, returning each as a map - The inner
{{- range . -}}
accesses the map itself
Create shortcode: /layouts/shortcodes/testimonials
We’ve made the partial, but those only work in .html
files. For .md
files we need a shortcode, so we make a simple wrapper (all it does is call the partial, passing the same context via the dot).
This may seem redundant, it’s just a pattern I like to follow as I often end up wanting to use the same functionality in both .html
and .md
files. If I make the shortcode first (as I used to) then when I decide I need a partial I have to convert the shortcode to a partial then make a wrapper like this… it’s happened enough that I just do it this way by habit now.
{{/* /layouts/shortcodes/testimonials */}}
{{- partial "testimonials" . -}}
Usage
To add the testimonials to a page we just add {{< testimonials >}}
to the corresponding .md
file in our Hugo site.