Setting Up a Website with the Hugo Framework

03 January, 2020

What is hugo?

Hugo is a static JAM generator written in Golang:

Why I switched to Hugo

This site used to run on WordPress, which had a lot of features that are completely unecessary for a simple blog. Hugo opts for a simple and streamlined approach with a tiny set of key functionalities, that can be easily expanded where needed.

Another thing I really dislike about Wordpress is the editor. I feel like I used to spend more time wrestling with the formatting than I did writing the actual content. With Hugo all content is written in Markdown, so formatting is explicit and works exactly like you think it will.

Lastly, because Hugo produces a static website and doesn’t need a database, the entire website can be stored in Git and served from just about anywhere.

Install Hugo and initialize a new site

Installing Hugo by following these instructions for your appropriate OS.

Create a folder for your Hugo project and initialize a new site called personal-blog:

$ mkdir website && cd website
$ hugo new site personal-blog

Congratulations! Your new Hugo site is created in /path/to/project/personal-blog

We now have a folder called personal-blog that houses the code for the website.

A comprehensive explanation of the folder structure can be found here.

Install a theme

Hugo has a robust built-in theming engine, but the fastest way to get going is by picking one of the hundreds of pre-made themes found here.

Installing and swapping between themes in Hugo is very simple: Just move the source files to the themes/ folder located in the project’s root directory.

For this guide we will be using a theme called Manis:

// Git clone theme from source

$ cd themes/
$ git clone

Cloning into 'manis-hugo-theme'...
remote: Enumerating objects: 55, done.
remote: Counting objects: 100% (55/55), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 1064 (delta 18), reused 35 (delta 11), pack-reused 1009
Receiving objects: 100% (1064/1064), 1.43 MiB | 2.83 MiB/s, done.
Resolving deltas: 100% (522/522), done.

To activate the theme, we need to add it’s name to configuration.toml:

$ echo 'theme = "manis"' >> configuration.toml

To see the website in action, start a local Hugo development server:

$ hugo server -D

                   | EN  
  Pages            |  7  
  Paginator pages  |  0  
  Non-page files   |  0  
  Static files     |  7  
  Processed images |  0  
  Aliases          |  0  
  Sitemaps         |  1  
  Cleaned          |  0  

Built in 4 ms
Watching for changes in /path/to/project/personal-blog/{archetypes,content,data,layouts,static,themes}
Watching for config changes in /path/to/project/personal-blog/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address
Press Ctrl+C to stop

The -D flag tells Hugo to include pages marked as drafts.

Hugo will watch your project directory, and rebuild the site whenever changes are detected.

Add content

Time to add some content. Create a new folder called blog/ inside the content folder:

$ mkdir content/blog
$ hugo new page blog/
/path/to/project/personal-blog/content/posts/ created

This will create a new Markdown file under /content/blog/ called

Let’s take a look at the structure of that file:

title: "My First Post"
date: 2019-03-26T08:47:11+01:00
draft: true

Data contained between the lines is called front matter, and contain arbitary meta-data about the post, that can be accessed by in templates.

Set up navigation

You might notice we don’t yet see our newly created post anywhere.

This is because most themes have their own default configurations, that might not line up with your naming scheme.

Fix this by first overriding the default config with the theme’s config.toml.

Now find the [menu] block. Here’s what it looks like in the Manis theme:

                name = "Post"
                url = "/post/"

                name = "About"
                url = "/about"

You migh have already have figured out that each file and folder starting from content/ corresponds to their respective URL paths.

Here’s some examples to illustrate how the folder strucutre corresponds to url paths. The slugs can be set in the post’s front matter as well, so the name of the file doesn’t necessary matter.

/about/            -->  content/
/posts/blog-post/  -->  content/blog/
/guides/hugo/post/ -->  content/guides/hugo/

You should be able to access the /posts/ route now.

Deploy the site

Hugo produces a static HTML-bundle, so the site can be hosted from almost anywhere. To build the static bundle, run this command:

$ hugo --minify -d build

Setting up hosting is left as an exercise to the reader.

Closing thoughts

Hugo is a great solution for people who want a minimal, fast and extendable static site generator.

This minimal SSG philosophy Hugo follows also comes with constraints. It’s likely to be too difficult to be used by non-techincal people, as there is no GUI. If you need to do complex DOM manipulation or dynamic content, you might be better off using something like NextJS instead.