Tips for Building a Site with Hugo
The documentation for Hugo is generally very good, but I ran into a few areas where it left me confused, or useful things were missing. I thought I would write up some of things I struggled with (and will possibly add to the official docs).
Some of these were down to migrating from an existing site and wanting to match things like URLs.
Variable Casing
I found it often confusing, sometimes bewildering, as to why certain variables weren't appearing or evaluating, only to find the casing wasn't correct.
In the layouts, variables are generally camel-cased (except for parameters in the front matter, which are all lowercase), but then you get things like RSSLink
rather than RssLink
and BaseURL
rather than BaseUrl
.
This is also true of the config file used to control the site. I found baseurl
to work, rather than baseURL
as stated in the docs (although the examples have it all in lowercase). A seemingly missing variable is rssURI
, which defines the filename to use for the generated RSS feed (see below) and completely fails all the other 'standards' for variable names.
Conditionals
I struggled with the formatting of conditional statements, which are inherited from Go's template functionality. Instead of the usual format (which even Go uses), like this:
if condition_one and condition_two then
The conditions are front loaded, unless there are more than one, so this:
if not (eq .Title "Example")
is the equivalent of:
if .Title != 'Example'
and
if or (eq .Section "blog") (eq .Data.Singular "category")
is the equivalent of:
if .Section == 'blog' or .Data.Singular == 'category'
There are a few other examples in the Go Template Primer section to wrap your head around.
Template Paths
Another bit of inconsistency caught me out on this. The default path, for most layouts in a theme, is:
/themes/THEME_NAME/layouts/_default/LAYOUT_TYPE.html
If you want to override that for a particular archetype or section you can supersede the default by using:
/themes/THEME_NAME/layouts/TYPE_OR_SECTION_NAME/LAYOUT_TYPE.html
If you want to create a list page though (for your blog posts, for example, or everything in a particular archetype) you have to use:
/themes/THEME_NAME/layouts/section/SECTION_NAME.html
(assuming you want to override the default list or section view)
Lastly, if you want to create a list of terms (all categories assigned to a post say) you need to store it under:
/themes/THEME_NAME/layouts/taxonomy/SINGULAR_NAME.terms.html
So if you're wondering why your content isn't displaying correctly, check where you put the template file and what you called it.
Partials
For those more used to WordPress, these are the equivalent of includes: files that contain specific layouts or code to be pulled into a larger template. A header, footer and sidebar are all prime examples.
To pull one into a template, you use:
{{ partial "path/to/template" . }}
I was left scratching my head for a while before realising I was missing that trailing dot (full stop/period). My variables weren't working, even the global ones. The reason is you need to pass the variables to the partial template. The dot passes everything in the current context so it's very important.
There's also the option of using .Render on list views, though I have no idea why there's two approaches, just make everyone use partials (which is what I ended up doing).
Archetypes
For a WordPress user, these are akin to custom post types. They allow you to create templates with variables in the front matter of the Markdown files generated with you use the new command. It essentially saves you having to manually add the variable names each time (you'll still need to fill them in manually).
Date Formatting
Functions in general are a bit confusing because some have the function's name at the start (e.g. {{ lower "BatMan" }}
), while others come after a pipe character (e.g.{{ .Title | markdownify }}
). With some functions you can probably do either, although I never put that to the test.
The dateFormat function looks like this (in the docs):
{{ dateFormat "Monday, Jan 2, 2006" "2015-01-21" }}
The first parameter is the template for the format and the second is the date you want to format. I actually ended up using this:
{{ .Date | dateFormat "2 Jan 2006" }}
Which operates on the .Date variable to turn it into a date of that format.
I was concerned about how to specify the date format, how would it know what was the day and what the month (as the crazy Americans have it the wrong way round)? Well Go's time package looks for a specific date:
01/02 03:04:05PM '06 -0700
So Monday 2nd Jan 2006, which allows it to know what is what. Just make sure you set your format up to match that date if you're doing something non-standard and it should work it out.
One thing that's missing from the date format is ordinals (that's down to Go though).
Permalinks
There's a whole section of the docs dedicated to how to format permalinks, which you can do on a per-section basis.
My advice is to use :slug
rather than :title
in your URLs. If you don't set it in your front matter Hugo will fall back to the title, so you don't have to do it manually each time, but it means you can set the slug parameter in your front matter to override the title should you need to.
I found this especially helpful when Hugo decided a full stop (period) should remain in the URL path and promptly broke the link (commas it converts to hyphens).
RSS Feeds
There are plenty of as-yet undocumented features and options in Hugo (or so it felt when I was trying to solve various things), one of which is specifying the URL of your RSS feed. It's not covered in the Configuration section, but you can add:
rssURI = "feed"
(If you're using the TOML format, you'll have to add the equivalent if using YAML.)
This tells Hugo to output the RSS feed to:
http://yourdomain.tld/feed
Which seems to work (I was trying to match WordPress' path). Obviously put in rss.xml and you'll get:
http://yourdomain.tld/rss.xml
I believe the default is index.xml if you don't specify anything.
Debugging
Another useful tip is printing out the variables in a context to see what you have and how to access them. There's a section in the docs that covers this, plus the echoParam function to make this a bit easier.
Lastly
When all else fails, have a search through the Hugo community forums, as you'll often find the answer in there.