Creating a Personal Site With Hugo

Confused by Hugo? I’ll show you how to create your own personal site which has multiple sections to help keep your content organised. We won’t use a theme but instead build our own site from the ground up.

Lets assume we want to create a site called Brutal Deluxe for a designer who wants to:

  1. have a home page to greet visitors
  2. write long form insightful articles on all things design
  3. maintain a daily journal
  4. organize 2 and 3 into their own separate sections on the site

Creating the Hugo project

First let’s create the project and call it “brutal_deluxe”

hugo new site brutal_deluxe
cd brutal_deluxe

You’ll notice hugo created the following files/folders

archetypes/
config.toml
content/
data/
layouts/
resources/
static/
themes/

To create this site we only need to concern ourselves with the layouts and content directories.

Viewing changes locally with Hugo’s development server

While we develop the site locally we will want to be able to view it in the browser. Hugo provides a command to run a local server and rebuild your site as you make changes. Run the following command (I’ll explain the -D part later):

hugo server -D
Building sites …
<lots of output redacted>
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)

Creating a homepage

Viewing http://localhost:1313/ in your browser will simply show a blank screen. That’s because we haven’t defined a template for the homepage. Lets do that by creating the file brutal_deluxe/templates/index.html and addd the following content:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>Brutal Deluxe</h1>
    <p>
      Welcome to my site.
      I like writing articles about design.
      I also keep a daily journal.
    </p>
  </body>
</html>

Refresh the browser page and you will see we now have a home page.

Creating content using Hugo’s sections

Let’s create our first article titled “Why orange is the new black”. As this is an article and not a journal entry we’ll specify we want to create it as a part of the article series. Hugo calls series or collections of content a section. Lets create our first content for our articles section:

hugo new articles/why-orange-is-the-new-black.md

You will see we now have a new articles directory and markdown file at brutal_deluxe/contents/articles/why-orange-is-the-new-black.md. At the top of the file is a config area which Hugo uses when generating the site. The actual content for the page should come below it so let’s do that now.

---
title: "Why orange is the new black"
date: 2020-04-18T15:37:28+09:00
draft: true
---

Orange is rad!!!

Next we need to define templates to control how our articles are rendered. For discoverability we need show visitors a list of all the articles. We also need to show a single article in full when a visitor clicks on one from the list. Hugo has two templates to do just this: list.html and single.html.

Displaying an index for a section

When Hugo generates your site it looks in various places for these two templates. One place it will look is a folder called brutal_deluxe/templates/_default. Inside brutal_deluxe/layouts create the directory and file _default_/list.html and add the following to it.

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>Latest Entries</h1>
  </body>
</html>

Open http://localhost:1313/articles and you will see the content above. Now lets display a list of all our articles. Update brutal_deluxe/templates/_default/list.html with the following:

<body>
  <h1>Latest Entries</h1>

  <ul class="posts">
    {{ range .Data.Pages -}}
      <li>
        <a href="{{ .Permalink }}">{{ .Title }}</a>
      </li>
    {{- end }}
  </ul>
</body>

Here we are using a little interpolation to loop through all the pages and for each page, render a link with the page’s title and permalink. Data.Pages will refer to all the pages in the section which the current page belongs to. When rendering our article “Orange is the new black”, Data.Pages will only contain a collection of articles and will not contain any journal entries.

Refreshing the page in our browser will now show all the articles (well in our case we only have one.)

If you don’t see it make sure you started the Hugo server with the option -D i.e.

hugo server -D.

This option tells Hugo to display pages even if they are still a draft. When you create a new page, like our article above, it is automatically set to be a draft so you will not see if you only use hugo server.

Displaying content in full

If we click on the article we get a 404 not found error. We can fix that by creating the single.html template and by adding the following html to it.

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <h1>{{ .Title }}</h1>
    {{ .Content }}
  </body>
</html>

Refreshing the page and we can now see our article being rendered in full.

Now lets turn our focus to the journal. Lets create a new entry for today via

hugo new journal/monday-17-April-2020.md

and add some content to the entry

---
title: "Monday 17 April 2020"
date: 2020-04-19T16:51:43+09:00
draft: true
---

What a great day!

If we visit http://localhost:1313/journal in our browser we’ll see our list of jounal entries and clicking on our entry will show it in full.

Hugo section templates

The only problem is it’s a little hard to distinguish between our articles and journal sections. It would be nice if we could have distinct titles for each section and perhaps in the future we’d want to have entirely different html and styling for each section. Currently we can’t do this because we are using one set of templates for both sections. Let’s change that by creating two different sets of templates.

Previously I mentioned Hugo will look in various places for tempates when rendering your content into HTML. Before Hugo looks for templates in the /layouts/_default it will first check to see if you have a directory matching the name of the content’s section and if so use the template files defined there.

Lets create two directories inside layouts/ called articles and journal. Then copy the layouts/_default/list.html template into these two directories. Next update the copied list templates to have descriptive headings like so:

# layouts/articles/list.html

<body>
    <h1>Latest articles</h1>
    <p>My thoughts on all things design</p>

    <ul class="posts">
# layouts/journal/list.html

<body>
    <h1>My Daily Journal</h1>
    <p>I daily record of my work and things I learnt.</p>

    <ul class="posts">

Now when Hugo renders the index pages for either section it will use these template files. When it renders an individual article or journal entry it will check in these directories for a single.html but won’t find one so it will then check the _default directory and find and use the single.html defined there. For our purposes this is fine but if you ever need to vary the presentation between these two content types we can simply define individual single.html templates in layouts/articles and layouts/journal.

Visiting both of these sections in our browser will now show the different layouts.

Now that we have those sections set up we should link to them from the homepage. Update layouts/index.html like so

<h1>Brutal Deluxe</h1>
<p>
  Welcome to my site.
  I like writing <a href="/articles">articles</a> about design.
  I also keep a <a href="/journal">daily journal</a>.
</p>

Hugo base templates

This setup will serve most peoples’ needs just fine. However for some sites our HTML templates could have a lot of repetitive markup. For example the <head> tag may have lots of meta and javascript tags. It would be nice if we could define this common markup once and switch in and out section specific markup at render time.

Hugo allows us to do this through what it calls base templates. Let’s create one now by creating a new file at layouts/_default/baseof.html. In this file we will add the HTML common to both our sections and use a little Hugo code to allow us to swap in and out section specific markup:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    {{ block "main" . }}
    {{ end }}
  </body>
</html>

The block "main" is a hook provided to us by Hugo and allow us to render section specific HTML in this area. We simply need to update our existing articles/list.html to define a “main” block with the desired HTML. layouts/articles/list.html now becomes:

{{ define "main" -}}
  <h1>Latest Article</h1>

  <ul class="posts">
    {{ range .Data.Pages -}}
      <li>
        <a href="{{ .Permalink }}">{{ .Title }}</a>
      </li>
    {{- end }}
  </ul>
{{- end }}

Similarly `layouts/journal/list.html:

{{ define "main" -}}
  <h1>Latest Journal Entries</h1>

  <ul class="posts">
    {{ range .Data.Pages -}}
      <li>
        <a href="{{ .Permalink }}">{{ .Title }}</a>
      </li>
    {{- end }}
  </ul>
{{- end }}

Then templates/_default/single.html:

{{ define "main" -}}
  <h1>{{ .Title }}</h1>
  {{ .Content }}
{{- end }}

and finally templates/index.html:

{{ define "main" -}}
  <h1>Brutal Deluxe</h1>
  <p>
    Welcome to my site.
    I like writing <a href="/articles">articles</a> about design.
    I also keep a <a href="/journal">daily journal</a>.
  </p>
{{- end }}

Refreshing the site in the browser and everything renders as expected.

Hugo partials

Whilst we’ve been able to DRY up the outer HTML of our site there are times where we need to show the same HTML buried deep inside our pages. For example perhaps we want to display a sidebar listing all of the designer’s recent projects. Hugo allows us to do this through the use of partials. Let’s create one for the sidebar by creating the following directory and file layouts/partials/sidebar.html:

<div>
  <h3>Latest Projects</h3>
  <ul>
    <li><a href="">ACME redesign</a></li>
    <li><a href="">Evil Corp Branding</a></li>
  </ul>
</div>

Now update our list.html templates to render the template. For example update /articles/list.html like this:

<h1>Latest Article</h1>
<ul class="posts">
  <!-- ... -->
</ul>

{{ partial "sidebar.html" . }}

Note that the final period in {{ partial "sidebar.html" . }} is not a typo and is essential. Visit the article’s index and you should now see the sidebar.

And that wraps up this very short introduction to creating your own site with Hugo.