The Hugo static website generator is an incredibly flexible site builder, but this very flexibility means that the way it matches page templates to page content can seem unclear. I’ve been using Hugo for some time now and I only ‘got it’ just recently. What follows is essentially a summary for my future reference, but hopefully it’ll help other Hugo users too.
Within a site project folder, Hugo adds two key folders: content and layouts. There are others, but these two provide the core functionality. Let’s look at contents first.
Any folder within contents is a Hugo ‘section’: a branch in tree of pages you are setting up. Typically a section will have a section index page and a number of subsidiary pages. Each of these pages’ contents are placed in a markdown file. Say you have a section called bibliography
that contains a file mycenae.md
. This page will be rendered as /bibliography/mycenae/index.html
.
The folder also contains two further files: crete.md
and anatolia.md
, respectively rendered as /bibliography/crete/index.html
and /bibliography/anatolia/index.html
.
We also need a page — to be rendered as /bibliography/index.html
— from which the reader can select any of these three bibliographies. Its content is placed in either _index.md
or index.md
. Which of these you choose depends on how you want the page rendered, which brings us to the layouts folder.
This folder is where your HTML page templates reside. It can contain folders for each of your sections; if so, Hugo will look in them for the relevant templates. Any it can’t find what it’s looking for there, it will look in the special _defaults folder. In fact, for simple sites, _defaults is generally all you need.
I’ll cover the assembly of templates shortly; for now it’s enough to know that you need two templates, section.html
and single.html
, and possibly a third, list.html
. This brings us back to content:
_index.md
pages will be rendered using eithersection.html
orlist.html
;section.html
takes precedence if they are both present.index.md
pages are rendered usingsingle.html
— this is usually what you’ll use if a section contains no pages other than the index.- Other
.md
pages are rendered usingsingle.html
. Recallmycenae.md
, above? That is rendered usingsingle.html
.
These are simply the fall-back templates; you may choose to have section-specific version of each of these in section folders under layouts.
You can override the template selection in a content file’s front matter. For example, you may want a one section page to use its own template: just add the layout key to its _index.md
or index.md
file’s front matter and reference the name of the alternative template. A case in point: thanks to Hugo’s rules of template precedence, most of my section pages use section.html
, but one of them has layout: list
in its front matter to force it to use list.html
instead.
I’ve talked about section indexes — what about the main index page, the home page? Well, it’s just another _index.md
file, this time placed in the contents folder alongside however many section folders you have:
project/contents/_index.md project/contents/bibliographies/_index.md project/contents/bibliographies/crete.md project/contents/bibliographies/mycenae.md project/contents/locations/_index.md project/contents/locations/crete.md project/contents/locations/greece.md
The equivalent template is the unique index.html
, placed in /layouts
or /layouts/_defaults
.
Note that the contents folder should have only one .md
file, _index.md
or index.md
.
Let’s have a quick look at template assembly. Hugo uses a file called baseof.html
as the foundation template. Here you can add HTML that’s common to all pages within a section, or by default (ie. it’s placed in _defaults rather than a layouts section folder) across the site. Within baseof.html
you can specify template units using the block command:
{{ block "<BLOCK_NAME>" . }}{{ end }}
You then define blocks called for each of your template types. For example, one section.html
contains simply:
{{ define "header" }} {{ partial "header.html" . }} {{ end }} {{ define "main" }} {{ .Params.headline }} {{ .Content }} {{ end }}
Blocks are declared with the define command. When Hugo needs a section template, it constructs one from section.html
and baseof.html
: the blocks defined in the former file are inserted into the placeholders in the latter file. Hugo then injects the content from the content file.
When combined with the partial command, which pulls in sub-templates from the partials folder within layouts, the define and block commands give you a powerful way of breaking HTML templates down into small, re-usable and manageable chunks. It’s much easier to edit a 5-10 line chunk of code safe in the knowledge that the update will appear everywhere it needs to than to edit the same code in a set of 200-line templates.
And by naming and preparing the templates correctly, you can be sure Hugo will pull in the correct one for each of your content files.