Design and Publishing Notebook

Moving to the Hugo Static CMS for Personal Websites and Blogs

The Business Catalyst EOL Announcement

When Adobe unexpectedly announced End of Life for Business Catalyst in early 2018, it was time to search for a replacement CMS and blogging tool. Which one would be the right choice for a personal website and blog?

Other Business Catalyst developers were actively investigating and reporting on alternative dynamic CMS options, many of which offered at least as many features as Business Catalyst, but in a more modern way.

Impressive many of them were, but then I asked myself: with only one author, is a dynamic CMS really necessary? Would a static CMS, hosted on my own computer, be just as effective, simpler and less expensive? After some thought, the answer was a resounding yes!

I was not a newcomer to static CMSs, having used the Frontier Website Framework extensively in the late 1990s and early 2000s.[1] Frontier was ahead of its time then, but had grown old and disappeared years ago.

In its place, for static websites, I had adopted Ruby Frontier, a mimic of the Frontier Website Framework written in Ruby. The combination of Business Catalyst for blogs (and hosting) and Ruby Frontier as a static CMS, although clunky, had sufficed for nearly ten years. But Ruby Frontier was going nowhere and then Business Catalyst announced EOL.

Time for a major re-think.

The Search for a New Static CMS

What was happening in the world of static CMSs in 2018? Was the idea still relevant in this age of dynamic CMSs? It turned out that the static CMS was well and truly alive, with a variety of newcomers vying for attention. As with many software choices, the problem is not an absence of solutions, but an excess. After perusing a couple of those “Top 5 Static CMS” kind of articles I soon trimmed the list down to Jekyll and Hugo, which describe themselves thus:

  • Jekyll is a simple, extendable, static site generator written in Ruby. You give it text written in your favourite markup language and it churns through layouts to create a static website.”

  • Hugo is a fast and modern static site generator written in Go [language], and designed to make website creation fun again.”

Requirements of a Static CMS

Any replacement for the Business Catalyst (for blogs) + RubyFrontier (for static pages) combination had to meet the following requirements:

  • Ability to host multiple independent blogs in the same website.

  • Ability to parameterise website structure — i.e. the absolute minimal set of templates/subtemplates/partials combined with smart code — to avoid duplication of templates and code.

  • Easy to use development environment and fast to render.

The Trials of Jekyll

With Ruby as my day-to-day programming language, Jekyll initially seemed the ideal fit, so I began the task of setting up a test website using Jekyll.

It didn’t take very long before Jekyll revealed its limitations (for my particular application):

Feature Required Jekyll Limitation
Multiple blogs Jekyll was originally a “one blog” system (i.e. only one blog per Jekyll site). Later versions added the notion of “collections”, but these do not have the required functions of a blog.
Non-date-based blog post urls Many blogging systems adopt the convention that blog posts are arranged by date and referenced via a folder hierarchy in a url such as mysite.com/2019/02/my-post.html. This was Jekyll’s default assumption for its built-in blog system. But my blogs did not use this type of url and I don’t think of these posts as being fundamentally date based — i.e. the date of publication is not so important that it needs to be reflected in the post url.
Out of the box reliable pagination, next/prev generation, recent post lists, etc Jekyll turned out to rather limited in this area. Pagination required a third-party plug-in.

The more time I spent investigating pagination and auto-generated navigation in Jekyll, the more complex it became. In fact Jekyll seemed to need plug-ins for anything beyond basic functionality. It was time to pause the work on Jekyll and re-think the way forward.

A Second Look at Hugo

Initially passed over as perhaps too complex and requiring me to learn the Go language, I decided to take a second look at Hugo.

I found a blog post by another Jekyll user who had made the transition to Hugo. It was titled “Migrating from Jekyll+Github Pages to Hugo+Netlify” and included the following statements:

“During the last 18 months, working on my Web site became a daunting task—be that for developing, redesigning it, writing a blog post, or making updates to my speaking and workshop pages. My then static site generator, Jekyll, is why. And a change has long been overdue…

“Jekyll became unbearably slow at compiling my Web site after every change I made… I knew I’d have to ditch Jekyll and migrate to a new generator at some point… None of the static generators I saw gave me everything I wanted and needed for my site. Until someone suggested having a look at Hugo.

“After reading a little into the content structure and organization section and learning how Hugo offers the ability to create many different content categories and sections, plus all the general flexibility it provides, I thought that this was the static site generator I’d always wanted and needed.”

This article, which is an excellent in-depth account of how to set up a Hugo website, was the encouragement I needed to take a deeper look at Hugo.

Things to Like About Hugo

After studying and digesting the Hugo documentation I wrote this list of things to like about Hugo:

  • Content files can be written in HTML or Markdown.

  • Content files can include front matter declaring any number of “variables” for that page. Front matter can be declared using YAML, TOML or JSON.[2]

  • A Page Bundle is a directory at any hierarchy within the content directory that contains an index.md file. Page bundles are equivalent to individual content pages but comprise their own sub-directory with associated files, sub-directories, etc. and so provide a way to group page resources together with page content.

  • Parallel content and layout directory structures can automatically associate layout templates with content subdirectories, which are called sections.

  • Very flexible search hierarchy for finding a template for any page with lots of options for overriding by degrees of specificity. You can also specify the layout for an individual page in the page’s front matter.

  • While a page’s section is determined by the location of the source file in the content tree, users can specify the page type in the front matter.

  • An _index.md (or _index.html) page in a section will render as index.html for that section, using a matching list.html template in the template search hierarchy.

  • If Hugo does not find an _index.md for a section, a matching list template will be rendered anyway, thus enabling “automatic” generation of index pages for sections.

  • The Go Templating Language allows specification of blocks which can be overridden in subtemplates.[3] Blocks are a powerful concept which permit a site-wide base template (baseof.html) to define the structure of a generic page. Sub-templates can selectively override any of the declared blocks within the generic template. This ability to generalise page structure and DRY up your template code is a game-changer.

  • Hugo Partials are just like partials in Rails — re-useable template components.

  • Hugo Shortcodes are a special kind of template containing code snippets which can be called (like a function) with parameters from a Markdown or HTML content file.[4] They exist to circumvent the limitation that you cannot include Go Templating Language inside a content file. There are built-in shortcodes or you can define your own custom shortcodes.

  • Hugo builds an ordered collection of the content files in each section. It is easy to generate lists of the pages in a section and “next” and “previous” navigation links for any page.

  • If you require it, Hugo has built-in pagination for the content files in each section.

  • Very flexible multiple output formats and support for multiple languages.

  • Aliases specified for a page support mapping old urls to new ones when transferring sites to a new rendering system with different url conventions.

These are just some of the features which stood out in my initial review.

The architecture of Hugo could easily be done in Jekyll with Ruby, but I had gained the sense that Jekyll is constrained by historical decisions made during its initial design. Hugo seems to have learned from numerous earlier efforts and is a more recent piece of work.

The more I looked into Hugo the more I liked it. A very sophisticated and well designed system. The decision was made — Hugo it would be.

In a future post I’ll look at my experience using Hugo.

Footnotes

  1. One of the first content management systems (CMS) appeared circa 1996. It was the Website Framework built by Dave Winer using his Frontier software, a tool which had debuted some years earlier as a scripting system for the Apple Macintosh. You can read more about Frontier in an earlier post “Hierarchical Attributes in Website Structure”.

    The Frontier Website Framework was a static CMS. It married together content for individual pages with “page templates” and other components, including arbitrary scripts written in the UserTalk language, to render complete html pages. But these pages were not on the public World Wide Web. You had to copy them to a web server using an FTP tool.

    Eliminating this copying step was the motivation for Dynamic CMSs, with Dave Winer and Frontier once again being one of the very first to pioneer this idea circa 1999.

    The number and sophistication of Dynamic CMSs has exploded over the past twenty years with some clear winners and losers. This process shows no sign of abating, with CMSs now being superseded by “online business” tools, of which Business Catalyst was one of the first, that combine a CMS, email marketing, ecommerce, blogs, and a slew of other functionality.

  2. Page-level variables declared at the start of a page were another idea already present in the Frontier Website Framework circa 1996.

  3. This idea has been seen before in the Dryml markup language which is part of the Hobo extension for Ruby on Rails, which dates from 2009 or earlier. Dryml allows you to define an html-style tag and then “call” that tag while at the same time “reaching inside” the tag definition to override only parts of its definition. Dryml brought the familiar concept of overriding methods in a subclass to the world of markup languages for the View layer. Hugo’s blocks are not as flexible and elegant as Dryml, but then nothing I’ve seen so far is.

  4. Shortcodes have a parallel with custom Dryml tags because shortcodes can take parameters in the same way that Dryml tags can take parameters.