Noobs guide to Hugo
✍️ Wanna start a blog?#
It’s wonderful to start blogging, and these days, the Internet has made it extremely easy to set things up. There are various tools available that can help to achieve the same goal, while meeting different needs.
If you don’t really bother building things from scratch and only care about distribution, Medium is the first start. It has millions of readers, an engaged community, and the editor is simple and elegant. Personally, I also like Notion for its functionality and multi-use case. You can easily share anything in Notion publicly, which basically serves as a blog/website. The only downside is that Notion doesn’t support custom domains yet.
However, if you fancy more customisation without too much coding, WordPress, Squarespace, Weebly and Wix are among the popular options. I’ve also discovered Format as a platform for creatives; and Webflow, a design-first dynamic website builder without any code, is gaining lots of traction from the rise of the no-code movement.
After all, there’s no single tool that fits all, and most often it comes down to personal preference. Go for whatever that’s easy for you to use and to reach wider audience.
🤔 Why Hugo?#
One of the benefits of working in tech is that you’re constantly surrounded by smart people searching for alternatives - I got inspired by a coworker and started exploring alternatives to host my website without the need to pay. Then I found Hugo - a general purpose website framework written in Go. As a static site generator, Hugo is extremely fast to deploy.
I like the idea of learning through doing, and since Hugo supports both HTML and Markdown as content formats, it can help me get familiar with both, and understand how websites are built and deployed. Further down the road, I can add code blocks and enable math formulas for data related posts!
Ok, I should admit that I chose Hugo over Jekyll because Hugo has more themes that suited my taste; and I passed over Gatsby because the learning curve (JavaScript, React and GraphQL) is too steep 😂.
Anyway, after 6 months of procrastination, I’m pretty happy about the result (since last Christmas✌️). Depending on the theme you choose, different levels of customisation may be needed to make it work. I will go through the major ones in this post and hopefully this will make life easier for those keen to try out!
👩🏻💻 Hands on keyboard#
Hugo has a well-documented guide here to set things up, with the assumption that you are comfortable with Git and the command line. The example here uses MacOS and the theme Hugo Novela. I will leave out any stylings so the code can be applicable elsewhere.
1. Create a site locally#
This is the first step to building your own website before making it live on web:
- Install Hugo on your machine using Homebrew
brew install hugo
- Go to your designated folder where you want your new site to be created
-- for example, I would like to have mine under github folder
cd ~/github/
-- use the command line below to create a new site
hugo new site your_site_name
- Choose a theme from themes.gohugo.io, here I use Hugo Novela. Alternatively, you can also download it directly via
https://github.com/forestryio/hugo-theme-novela/archive/master.zip
and move tothemes/
cd your_site_name
git init
git submodule add https://github.com/forestryio/hugo-theme-novela.git themes/novela
- Then add
'theme = "novela"'
toconfig.toml
to let Hugo know that you’re using this theme. You can also choose to have your config in.json
or.yaml
.
2. Site structure and your first post#
Now that you’re all set, it’s time to start create a new post and understand how Hugo structures the website:
- Folders and files under
content/
will be rendered as web pages organised as follows
contents/
post/
post-01.md
post-02.md
_index.md
about.md
Images are stored in
static/images
and can be added to the post using MarkdownThe theme is normally organised as default layouts containing “partials” that enrich the content in defaults
layouts/
_default/
baseof.html
list.html
single.html
partials/
page/
author/
article/
head/
...
To generate a new post, you can either create a new
.md
file inpost/
or usehugo new post/my-first-post.md
The newly generated post normally contains the following, and you can customise this by tweaking
archetypes/default.md
---
title: "My First Post"
date: 2020-01-04T22:02:43+11:00
hero: /images/hero-3.jpg
excerpt:
tags:
draft: true
---
This is my first post!
- With Hugo, changes such as adding new posts, CSS styling and etc. can be instantly viewed locally at
http://localhost:1313/
with the following
-- with draft enabled
hugo server -D
-- without draft
hugo server
3. More customisation - tags and math#
Before making the site public, you probably want to make it more personal. config.toml
is the good starting point, where you can change the URL, site name, enable tags/categories, add additional pages and etc. Here’s my current config, noting that I use the novella
theme:
baseURL = "https://www.example.com"
languageCode = "en-au"
title = "YOUR SITE TITLE"
theme = "novela"
paginate = 6
enableRobotsTXT = true
canonifyURLs = true
enableEmoji = true
[menu]
[[menu.main]]
identifier = "about"
Name = "About"
url = "/about/"
weight = 0
[taxonomies]
tag = "tags"
author = "authors"
preserveTaxonomyNames = true
[permalinks]
post = "/blog/:title/"
I have included two additional features to this theme so that I can add tags to posts and collate them under corresponding tag pages, and use math formulas in Markdown where necessary. If your theme is already supporting these features below, you can skip to the next step on hosting and deploying the site.
🏷Tags
Tags are helpful for readers to find posts under the same topic. Hugo has built-in support called taxonomies for user-defined groupings of content. If you copy the config setting from above, you can define tags in your post with tags: ["tag_01", "tag_02"]
. By doing this, this post is now associated with both tags.
The next step is to render pages with posts under common tags and also a page containing all the tags. In fact, when you create tags in a post, Hugo will create list pages like \tag\tag_01
. However, we still need to combine everything together through taxonomy list templates and partial templates.
- Render tags in posts
I borrowed the code from here, and created a file under layouts/partials
called tags.html
. The code grabs tags defined in a post and links them to associated tag pages.
<!-- layouts/partials/tags.html -->
{{ $taxonomy := "tags" }}
{{ with .Param $taxonomy }}
<ul>
{{ range $index, $tag := . }}
{{ with $.Site.GetPage (printf "/%s/%s" $taxonomy $tag) -}}
<li>
<a href="{{ .Permalink }}">{{ $tag | urlize }}</a>
</li>
{{- end -}}
{{- end -}}
</ul>
{{ end }}
Then you can add the following partial to layouts/post/single.html
. For this theme, I added it between the article content and the next article’s partials.
<!-- layouts/post/single.html -->
<!-- ... -->
{{ partial "tags.html" .}}
<!-- ... -->
- Create list page of articles under a tag
After that, your tags should show up in the posts if defined. The next step is to reformat the default list pages so that they can display articles associated with specific tags. The original list.html
is simple and renders all articles in the whole website. The logic is added below so that if a page is related to \tag
, it will only render posts associated with that tag in this list page.
The code modified from hugo-notepadium.
<!-- layouts/_default/list.html -->
{{ define "main" }}
{{ partial "articles/hero.html" . }}
{{ if isset .Data "Term" }}
<h2><span class="hashtag">#</span>{{ .Data.Term }}</h2>
<ul>
{{- range .Data.Pages -}}
{{- if (in (.Site.Params.excludedTypes | default (slice "page")) .Type) -}}
{{- else -}}
<li>
<div>
<h2>
<a href="{{ .RelPermalink }}">{{.Title}}{{ if .Draft }}<sup class="draft-label">DRAFT</sup>{{ end }}</a>
</h2>
<div>
{{ if $.Site.Data.month }}{{ index $.Site.Data.month (printf "%d" .Date.Month) }} {{ .Date.Year }}{{ else }}{{ dateFormat "January 2, 2006" .Date }}{{ end }}</span>
{{ if .Params.timetoread }} • {{ .Params.timetoread }} min read{{ end }}
</div>
<span>
{{ if isset .Params "excerpt" }}
{{ .Params.excerpt }}
{{ else if gt (len .RawContent) 120 }}
{{ slicestr .RawContent 0 120 }}...
{{ else }}
{{ .RawContent }}
{{ end }}
</span>
</div>
</li>
{{- end -}}
{{- end -}}
</ul>
{{ else }}
{{ partial "articles/articles.html" . }}
{{ end }}
{{ partial "footer.html" . }}
{{ end }}
- Generate the master tag page
Finally, we also need a standalone tag page that aggregates all tags, with links to individual ones. This is achieved by creating a new HTML file as layouts/_default/terms.html
.
The code modified from hugo-notepadium.
<!-- layouts/_default/terms.html -->
{{ define "main" }}
{{ partial "articles/hero.html" . }}
<h2>{{ .Name }}</h2>
<p></p>
<p>
{{ $biggest := 1 }}
{{ $smallest := 1 }}
{{ $max := 3 }}
{{ $min := 1 }}
{{ $size := $min }}
{{ $data := .Data }}
{{ range $key, $value := .Data.Terms.ByCount }}
{{ $size := (add (mul (div $value.Count $biggest) (sub $max $min)) $min) }}
{{ $size := (cond (eq $biggest $smallest) $min $size) }}
<a class = "article-tag li" href="{{ $.Site.LanguagePrefix | absURL }}{{ $data.Plural }}/{{ $value.Name | urlize }}/">
<span class="hashtag">#</span>{{ $value.Name }}<sup>{{ $value.Count }}</sup></a>
{{ end }}
</p>
{{ partial "footer.html" . }}
{{ end }}
After all these, the tag feature should be working for the entire site 🤞, and the same steps can be applied to additional taxonomies such as categories and authors.
🧮 Math syntax
If your theme has no support for mathematical notation, here is an easy way to have it enabled through a third party math typesetting library called KaTeX.
- Create a partial
math.html
underlayouts/partials
<!-- layouts/partials/math.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_SVG"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
showMathMenu: false, //disables context menu
tex2jax: {
inlineMath: [ ['$','$'], ['\\(','\\)'] ]
}
});
</script>
- Include the following partial in the template that renders posts, in my case, it’s
layouts/post/single.html
<!-- layouts/post/single.html -->
<!-- ... -->
{{ if or .Params.math .Site.Params.math }}
{{ partial "math.html" . }}
{{ end }}
<!-- ... -->
- Lastly, to enable KaTeX globally, set
math = true
in the config; else, setmath: true
per individual post. Below is an example of a logistic function:
$$ \sigma(t) = \cfrac{1}{1 + e^{-t}} $$
4. Running it live on the Internet#
Hugo can be hosted virtually anywhere. I chose Netlify for its ease of use. Other popular hosting solutions will be added to the reference links at the end.
All you need to do is connect your GitHub account with Netlify, and then authorise Netlify to access the website repo for continuous development. You probably also need to configure another file netlify.toml
to specify your Hugo version. For more details please see here.
💭Final thoughts#
Congrats on following along and creating your own website 👏!
To be honest, I never thought I’d be able to do all of these, let alone write a tutorial! The process was a lot more cumbersome than simply paying for a service but I do find it satisfying and rewarding in the end. Despite some hacking around, I got to understand a lot more about how a website works, fixing bugs one by one, and most importantly, realising that nothing is unlearnable (and Google is your good buddy!).
The site itself is far from perfect but I’ve decided to take a break from development, instead, to spend more time reading and synthesising. It is only the first week of a new decade and I can’t wait for what’s more to come!
Useful resources and references#
- Intro to Markdown: https://commonmark.org/help/tutorial/index.html
- GitHub Learning Lab: https://lab.github.com/
- Add Tags to Hugo: https://www.jakewiesler.com/blog/hugo-taxonomies/
- More on Taxonomies: https://gohugo.io/content-management/taxonomies/
- KaTeX cheatsheet: https://katex.org/docs/supported.html
- More on hosting and deploying: https://gohugo.io/hosting-and-deployment/
- Twitter cards: https://gohugohq.com/partials/twitter-cards-partials-for-hugo/
Update and Acknowledgement: As of March 2020, I have switched my theme from Novela to this one created by Jake. This site would not be possible without his amazing work 🙌.
Jake has recently open-sourced the theme for Hugo community! Check it out if you’re interested!