Hugo from scratch 2014-05-18

Description: notes on creating a hugo based site from scratch

Reading Time: 6

Tags: Webdev

Hugo is a blogging engine written in go.

I’ve wanted to try a static blogging engine for a while, because the entire concept appealed to me - generate a bunch of static pages which can be delivered fast from any web server. Also, historically I’ve used Wordpress which was slow, hard to update, and has a somewhat toxic community around it, and my other hammer of Drupal (which I use extensively elsewhere) seemed like overkill for a blog.

I looked at some other options, and while many were more mature, they also tended to be in languages I didn’t find particularly appealing, or had a ton of dependencies, and if I’m going to go minimal with a blog engine, I’d like to go minimal with the implementation as well.

Setup notes

I used both the binary distribution of versions 0.1.0 and 0.1.1 (which is much improved).

Hugo uses Blackfriday to process markdown into HTML, which supports most common Markdown extensions, tables, etc.

For metadata on a post/page, you create Front Matter. This can contain a whole lot of data, but it’s up to you to enter it properly.

You can run Hugo’s internal web server by going to your site directory (or pass that with -d path/to/dir argument) and running the following command, which will watch for changed files and update as necessary:

hugo server -w -v

Then you can visit the site at: http://localhost:1313

Note that the server doesn’t watch for a changed config.* file in the site dir - change that and you have to manually restart the server.

Doing things the hard way

I generally don’t like using a premade template when exploring a new web framework, in favor of making my own from scratch so I have a better understanding of how everything goes together, find the rough edges, and thus can better fix breakage in the future.

The minimal directory structure for a single page site that doesn’t throw errors when Hugo is run on it appears to be:

/               <- Base directory with all files
/config.yaml    <- Config file for things like site name, etc
/content/       <- Content (markdown to be processed into the site)
/layouts/       <- Templates (HTML whole and partial templates)
/layouts/index.html  <- The main site index
/static/        <- Static, unmodified content (css, images, etc.)

When you run Hugo in this directory, it will create a public directory that contains the processed, static version. It doesn’t appear to delete files created in public if they’re deleted in the other directories, which could leave behind files you didn’t want if you changed the directory structure, move files, change dates on post, etc.

To get beyond a single page, you need to create a few more files and directories:

/content/section/              <- The section name.  This should be singular
/content/section/item.md       <- A post/page/etc. 
/layouts/section/single.html   <- The HTML template for a single page of this section type

The single.html template is special in Hugo - it expects these to exist for every section type. If you have multiple sections that use the same single.html template, you can create it in the /layouts/_default/ directory, and it will be used for every section without a template.

Indexes

Hugo has the concept of Indexes, which are basically key/value stores you can access throughout the site.

Some of these are generated by default, such as .Data.Pages which contains all of the content in all the sections of the site.

One of the problems I ran into regarding Indexes was with the home index page. Most of the examples use something like:

{{ range first 10 .Data.Pages }}
    {{ .Render "summary"}}
{{ end }}

For the homepage template, with the goal being to show the summary of the 10 last modified bits of content on the site on the homepage.

If you paste this code into your otherwise empty site, it will do nothing, even if you’ve already created sections and content in them, and you will be confused as I was.

The reason for this requires an explanation of what the .Render command does - it goes into /layouts/section/ and looks for a file named (in this case) summary.html and renders it for each content type. This is called a view.

My initial way of attempting to fix this was to create a /layouts/_default/summary.html, but that doesn’t work - .Render doesn’t check for a fallback in _default, at least in the version I tested with.

Thus, you will need to create a template for every content type you want .Render to deal with. The side effect of this is that items without a summary be excluded from the .Render, which may be desirable, or you could split things into different summaries by doing a .Render "summary_section" or similar, where section would change.

Organizing Content

Apparently, if you supply a permalink pattern, you can organize your content anyway you want, and Hugo will override your organization with the specified pattern.

This is neat, because you can have files organized by date or subject matter in a subdirectory, but they all come out using the same organized pattern.

How do I make Hugo…

Dump variables during testing:

Various other languages have tools to dump variable content (pprint, var_dump, Data::Dumper, etc.).

printf "%+v" is the golang equivalent of these, and as the variables pages is pretty sparse on how data is actually structured in Hugo, I’ve found this to be helpful when doing printf debugging:

{{ .Site | printf "%+v" }}

This could also come in handy for other more complex formatting needs.

What I wish was better

Mixing of content and layout

The site home page (index.html) is in the layout folder, not the content folder. This seems out of place, although most home pages are frequently just templates.

File Extensions

The use of the .html extension on layouts that contain template data that isn’t HTML seems like a hidden landmine.

Similarly, the .md extension on content files isn’t configurable. I’ve been using .mkd forever. Apparently both are wrong.

The ability to shell out based on file type

It would be great to be able to run an arbitrary shell command on specific file extensions. For example, I’d love for Hugo to run my Sass conversions, or compress/resize images, etc. Shelling out wouldn’t be as fast/portable as doing everything natively, but it could be great creating complex workflows and interactions.

The docs are by example and lack context

Most of what I wrote above was due to a lot of experimentation and trial/error. There’s a lot of “look at/clone the example”, which is great for getting you running fast, but not so great for fixing things when they break.

Most of the example code in the docs doesn’t have a context for where you’d put it, so there’s a lot of trial and error involved.

Server watch mode doesn’t ignore temp files

When editing pages, the server will print a bunch of messages like:

["path/to/ref/.hugo.md.swp": MODIFY]

If your editor keeps it’s swap files in the same directory as the edited file, but does nothing about them. This seems unneccessary.