Creating a Static Site with Hugo - Part Two

Posted on
8 min read | 1538 words

In part one I explained how I created this site, using the Hugo Static Site Generator, stored the files on GitHub and deployed the site using Netlify. In this post i’ll explain how I added more functionality to the blog including responsive images and comments. I’ll also discuss the CMS i’m using to make content creation easier.

First, a change..

I’ve made the Github repository for the site private today. While initially I liked the idea of showing my progress with the blog publicly, more recently i’ve realised that this includes all my content and i’m not sure that’s something I want.

I read Why My Blog is Closed-Source by Josh W Comeau in which he raises valid points about the lack of control: Anyone can fork and repurpose my site, content and all. While I don’t mind this regarding the site itself - and i’d welcome any feedback on my setup - I wouldn’t necessarily want my posts elsewhere.

Since I created the repository, it has been forked twice already and the intial posts I moved over from Wordpress during my testing are now sat in other repositories. I’m not overly concerned because they are public after all, but I have no control over them. I can reformat my originals and edit them in my repository but unless the changes are pulled those copies will still exist.

I’d like to think those people are also working on their sites and will clear out my content when they go live but it’s disconcerting all the same.

Josh’s article also points out that with all my content being public there’s no such thing as a ‘draft’ anymore. I’m still feeling my way around these new technologies and being a bit revisionist as I tweak posts. I also have things I want to write but they’re not ready yet. A public repository goes against that.

Hosting and Generating Responsive Images

One of the potential issues with storing my site files on GitHub is the risk of the repository becoming bloated with with lots of files. While i’m unlikely to ever hit the numbers that popular sites do, I know that lots of images could easily see my storage usage swell which might impact how responsive working with the repository is as a whole.

In the spirit of the JAMstack, i’m adding in functionality by using Cloudinary to host my images. I will likely store the odd image on GitHub like an author photo or stock featured image, but for all my posts it makes sense to store them elsewhere.

The nice thing about Cloudinary is it not only hosts the images but also allows you to perform transformations on them. Rather than having to store multiple sizes of the same image to cater to multiple devices, instead I can store one image and have Cloudinary return an appropriate size when the image is requested.

I followed the advice in Mastering Image Delivery with Cloudinary to use an img srcset to return the relevant image size depending on the viewport resolution.

My Single post layout template calls a Partial template, passing in the featured image url and the cloudinary base url:

{{ with .Params.featured_image }}
    {{ partial "images/single-image" (dict "image" . "baseURL" $cloudinaryurl) }}
{{ end }}

The partial then determines the correct size to request from Cloudinary and builds the <img> markup. There is a fall back for browsers that don’t support the srcset attribute.

<img srcset="
    {{ .baseURL }}/w_500{{ .image }} 500w,
    {{ .baseURL }}/w_710{{ .image }} 710w,
    {{ .baseURL }}/w_1000{{ .image }} 1000w,
    {{ .baseURL }}/w_1420{{ .image }} 1420w"
    src="{{ .baseURL }}/w_500{{ .image }}"

You can see how the url is built up using the Cloudinary base url, transformation to apply and the current image. For lack of a better approach i’m providing empty alternative text since these images a just additional colour for my posts. I would provide informative alt text on images within a post.

I’m also currently using the thumbnail approach for post list items on the homepage, though I may change that as it only returns a fixed size:

{{ with .Params.featured_image }}
    {{ partial "images/single-thumbnail" (dict "image" . "baseURL" $cloudinaryurl) }}
{{ end }}

<img src="{{ .baseURL}}/w_200,h_200,c_fill{ .image }}"alt=""/>

I’m aware that image transformations are also available in Hugo using Page Bundles, though I have not looked into those. While writing this post I also discovered Processing Responsive Images with Hugo by Laura Kalbag which makes use of these.

I also need to make the images within posts responsive. While i’m currently uploading images through the Forestry interface, I could be using a Hugo Shortcode to set the Cloudinary image transforms. Cloudinary also has a script you can embed to transform all the images in the page.

Adding a Comment System

A common feature of blogging platforms are the built in comment systems. These changes are are dynamic so not readily available in a static site, however, again, the JAMstack approach means this functionality can be added in.

The easiest way to do this is with third party services like Disqus and Hyvor Talk.

However, I decided not to go that route because:

  1. I would have less control over the style of the comment system
  2. There are privacy concerns (particularly with Disqus)
  3. These services generally operate on a subscription model and I want to keep costs down

I looked into a more DIY approach. Tania Rascia’s article Roll Your Own Comment System for a Static Site looked promising but I wanted to keep the list of technologies to learn small. A lot of the open source comment system projects I found also assumed server hosting.

However, in my early research into Static Site Generators, I came across JAMstack Comments Engine, an example implementation of a do-it-yourself comments system for Static Sites. That example is built using Eleventy and Netlify Forms, and uses Slack for content moderation. However my inexperience with Hugo meant I wasn’t particularly confident about hacking it together.

Then I discovered “Static” Comments with Gulp, Hugo and Netlify that outlined a Hugo-specific implementation (without the Slack integration) that was clearly explained and helped everything fall into place.

The comment forms on my site are linked with a Netlify form which when submitted triggers a Gulp task to fetch these comments and trigger the build command to rebuild the site. While the form data is saved into a JSON file that is then used when the page is built, this is only created during the build step; I don’t store this file on GitHub itself.

I can review and delete comments within Netlify. While it doesn’t have the full moderation ability of the original article (that article uses two Netlify forms to act as queues for approved/all comments), it’s fine for my purposes at the moment. In the future I may get some insight into how to implement similar from Moderated Comments System on a Static HTML Site which is also Hugo-specific.

Using a Content Management System

While I enjoy writing in Markdown - the human readable markup language used to style text - one of the strengths of platforms like Wordpress is the Dashboard area where users can create and curate their content.

With the “seperation of concerns” that comes with the JAMstack approach, several “headless” Content Management Systems exist. The idea is that unlike traditional CMS like Wordpress, these are not tightly coupled with the rest of the site, leaving you free to change the source and output of the data without changing the CMS.

I ended up choosing Forestry for it’s good integration wth Hugo though several other Headless CMS exist such as Sanity and Contentful. (I had a play with Sanity back in the summer and would recommend giving it a look).

Forestry dashboard showing list of posts

Forestry provides the functionality you would expect: I can create and edit posts and pages and upload my images. For the latter I chose the Cloudinary option though Forestry also supports hosting images on Github, AWS S3 and Netlify Large Media.

Forestry media manager

The forestry interface also supports Hugo menus and lets me visually create UI templates to match the Front Matter of my files. For example, I can choose whether to write one of my regular posts or a review post from within the interface:

Forestry create a post dialog

Forestry Front Matter editor

There are some things that I think could be better:

  • Posts are sorted based on name or the date last edited. Since i’ve imported and edited lots of my posts, the order shown in the UI doesn’t match the order of posting dates.
  • Exploring the media gallery is a pain for more than a few images. It’s easier to look up the image on cloudinary and set the correct url in the Markdown manually.

That said, it has been a pleasure to work with. I raised a query about using Archetypes which was answered swiftly and followed up with a tweak to the Forestry documentation. Colour me impressed!

Final Thoughts

I still have things to tweak with the blog, namely styles and images, but it’s good to be writing again. Pulling together the above technologies wasn’t as difficult as I expected and i’m enjoying the flexibility and control this affords.

Featured Image photo by Jess Bailey on Unsplash

Leave a Pawprint

Confidential, will not be shared with anyone or published here.