Hugo Is Boss

20 Jul 2020

Recently, as noted here, I recently rebuilt my website at short notice. Thankfully I was already tinkering with a new version of the site in the background, and that new version suddenly went into production sooner than I’d anticipated. I was a bit nervous as I’d just started rebuilding the site in a new tool, called Hugo, which at the time I was still getting started with, but I’m pleased to note that it has exceeded my expectations.

Still, there’s a bunch of things I wish I’d known then than would have got me going much more quickly, so I thought I’d document them here to save others time. The tl;dr is:

  • Don’t use a canned theme, as it will severely restrict how you can structure your content, not just how it looks.
  • If you’re making your own theme, you can use the page metadata to do very interesting things
  • You can add your own markdown extensions to make common content tasks easier

If that’s possibly interesting then read on, otherwise if you scroll to the bottom there’s a picture of a skateboard.

Context first: my old site was effectively just a single blog thing running on Textpattern, and it’d been that way for a decade or there abouts. Textpattern is a simple system that did what I wanted at the time and let me ditch the self written nonsense I’d been using for the decade before that (I assume thus this new system will be in place about a decade - so be sure to check back in 2030 to see what I’m using then). Textpattern was a php based thing I just installed and it used a MySQL database in the backend for storing everything (blog posts, comments, the CSS snippets I used to theme it, and so forth). It was fine, but I wanted to replace it for several reasons:

  • I was paying a bunch of money for hosting both the website and the database for a site that does not get much traffic and does nothing dynamic.
  • It is nice that Textpattern is simple, but it also made it hard for me to use it for anything beside just the blog.
  • You can’t edit posts offline, which means I end up writing them in one place and then moving them over, which leads to my next point…
  • It has its own markup language rather than using markdown like everything else, which I forget when editing offline

So I decided that at some point I’d switch to a much simpler static website, mostly to save me money, but also to let me start doing more with my own website again.

Hugo is a static website generator tool - that is to say you feed it a bunch of files with your posts written in markdown, and then it generates all the HTML files for you with nice index lists, individual post pages, etc. You just then need to copy these static files to your web server and you’re done. Indeed, with a static site you don’t really need a web server of your own these days - you can host them in Github Pages, AWS S3 storage, or Azure storage blog, to name but a few. This would cost a lot less per month, and mean I don’t need to maintain anything server wise. For those curious, I’m hosting this on Azure currently, and it’s costing me a few pennies a day to host.

There are many static website generators out there, but I initially picked Hugo as firstly I didn’t want to write my own, and secondly if the tool turned out to be limited in features it is written in Go, a language I use regularly, so if it came to it I could modify Hugo to my needs (it’s thus not been the case - Hugo turns out to be very flexible as is).

So what are the Hugo features that have made me happy with my choice after using it in anger for the last couple of weeks? Let’s run through them.

Hugo will use a “theme” to make all your web pages look pretty, and indeed they host a site where you can pick from a large range of existing themes. My first bit of advice is, assuming you know any HTML and can cope with writing a few templates, to just ignore this and make your own.

When most people think of themes they thing of what colour the pages are, what font they use etc. And whilst in Hugo your themes do control that, they do something far more important that is lost when you pick an existing theme: how do you want your website to layout content, and treat different types of content? This is controlled by the theme, so if you have even the slightest desire to have more than what the theme author wrote, then you’re stuck. In my mind Hugo only really becomes a tool to enable you to make your own website when you make your own theme.

To make that more concrete, on this site I currently have two different types of content: I have text posts and photo posts. Before I just had the text posts, but as part of having my content on my own site, I wanted to also publish the pictures I post to Flickr to this site too. However, I don’t just want pictures to appear as another blog post, I want them to have their own space on the site and to have the pages for both photo lists and the individual photos to be tailored to show photos, not just squished into a page designed for text. Hugo allows for what I want, but the themes that you can get from their theme site do not, they all make assumptions about the nature of the content you’ll be hosting, and none of that worked for my design goals here.

Thankfully, although theme design can be quite complicated if you want, it is well documented and you can find various beginner tutorials via searching. Designing a theme is mostly about writing templates into which Hugo will place your content, and if you’ve done any modern web development you’ll get up and running very quickly, but if you’re not a developer then don’t worry as it’s really not that difficult once you’ve gone through a tutorial or two.

So that’s my most important tip: don’t use a pre-existing them, make your own: the theme will control how you structure your content, not just how it looks, and that will control how useful the site is to you.

My next tip is to take advantage of the non-content data that Hugo lets you store each page, and have your templates use that to do interesting things.

So a typical Hugo page looks like this:

title: "My amazing blog post"
date: 2020-07-20T10:28:57+01:00
draft: true

Blah blah blah something something something.

The bottom section is where you put the markdown for your post, this is your content. The top section is what Hugo refers to as the front matter. By default when you create a new page it’ll add those three entries, but you can store anything you like here. By default it uses YAML, but you can use JSON too if you like. In your theme’s templates you would then write something like:

    <title>{{ .Title }}
    <h1>{{ .Title }}</h1>
    {{ .Content }}

And you’d get a fairly simple page with the right title and the content converted from markdown to HTML under the title. So far, so simple hopefully. You could display the date in there too if you wanted, and the draft field controls if Hugo will render this page when you build the site or not - by default drafts won’t be compiled when you build your site until you say it’s no longer a draft.

However, let’s look at what I do for my photos posts. If you want to try follow along, have a look at an example page here. In my photos I have lots of extra data in the front matter, as you can see here for the page I just showed you:

title: "Parked"
date: 2019-11-13T08:02:48
taken: 2015-11-29T00:12:44
draft: false
flickr_id: 49057946503
latitude: 37.765300
longitude: -122.421814
Make: Fujifilm
Model: X-E2
ISO: 200
FocalLength: 35.0 mm
ExposureTime: 1/750
FNumber: 1.4
LensModel: XF35mmF1.4 R
LensMake: Fujifilm
LensInfo: 35mm f/1.4
City: San Francisco
Country: United States
State: California
Spotted parted outside Four Barrel in the Mission District. Again, a simple image, but it appeals
to me - probably the lighting/colour, which is straight out of camera pretty much.

What’s going on here? Firstly note the content doesn’t match what’s rendered in the page! No where in the content section do I have the image displayed, but if you follow the link to the page there is an image, so where’s that coming from? I generate the image HTML from the front matter in the page template:

    <a href="{{ .Params.flickr_url }}">
        <img src="{{ .Params.flickr_id }}_large.jpg" alt="{{ .Title }}"/>

For various reasons I post all my photos to first Flickr and then sync them over to this site using a script. When the script downloads the images it stores them with the flickr ID in the name so I can easily reference them using the ID as I do in the above template fragment.

You’ll also notice that at the bottom of the page there’s a paragraph about where and when I took the photo, and what camera and lens I used that also isn’t in the content. Again, that’s all from the front matter generated in my template.

Why do it this way? It’s for flexibility: say I decide I don’t want that final paragraph but rather I’d like to have a table of stats about the photo instead - I can simply change my template once and all my photo pages will get the new look. There’s more fields in there than I currently use, but in theory one day I could add a javascript map to the page and use the lat/long stored in there. But there’s no real cost to having more things in the front matter than I use, so why not put it in there just in case? This is nice, because if I was storing this all a database I’d probably be weary of adding things I don’t need, but here it’s free so I do so.

As a more simple example I add a one sentence synopsis to the top of each blog post which is where the short description on the home page comes from.

Putting all this information in the front matter rather than just hard-wiring the content also lets me do one other trick, which brings me onto my final tip for getting started with Hugo.

Markdown is a nice and simple way to express your content, but occasionally you want to do something that markdown won’t let you express, so you fall back to HTML. However, Hugo can help you here with what it calls shortcodes. It’s basically a way in the markdown you can invoke a tiny template. Again, let me give you an example:

I inserted this image into this blog post by just typing:

{{< photo parked >}}

This invokes a template I’ve got called “photo” which looks like this:

{{ $fullname := printf "photos/%s" (.Get 0)}}
<div class="photo">
    {{ with .Site.GetPage $fullname }}
    <div class="listimage">
        <a href="{{ .Permalink }}">
            <img src="{{ .RelPermalink}}{{ .Params.flickr_id }}.jpg" alt="{{ .Title }}"/>
        <div class="overlay">
            <b><a href={{ .RelPermalink }}>{{ .Title }}</a></b>
            - {{ dateFormat "2 Jan 2006" .Date }}
    {{ end }}

Here when I do the photo shortcode, my photo template works out the name of where the photo really is (as I’m lazy), then works out the Hugo page object based on that, and from there I can generate all the HTML to show the image. Suddenly a page line this one are really easy to pull together - I just need to know the title of the image I want and in two words and some punctuation I have the image nicely placed in the page without having to know anything about where the image is stored, how to link it, etc.

That’s it for now, but I hope this shows you a little how powerful Hugo is, once you get passed the idea that you should be using one of their default themes. I really is powerful under the hood if you want it to be and if you let it.