Home

Build a Website With Hugo

 · 22 min · Nasir

Learn how to build, design and deploy a simple blog using Hugo in this tutorial.

blog hugo tutorial

“Easy to build, fun to customize, and the loading time is just super-fast!”

What is Hugo?

Hugo is a Static Site Generator (SSG) that is getting traction in the world of website-building. As a static site, there are some limitations to its functionalities. But here are some of the reasons why I love to use Hugo:

  • very fast
  • easily customizable
  • simple to understand & start
  • seamless integration with third-party APIs
  • did I mention it’s fast?

In this post, I shall share how to build a simple, functional blog that you can style yourself with a bit of CSS using Bootstrap 5.

Using a framework like Bootstrap or Bulma makes website building a lot faster so that you can focus on creating content!


Why is Hugo fast?

  1. Written in Go (Golang)
  • Go is a programming language created at Google which is expressive, lightweight, concise, and efficient. One of its best feature is its concurrent processing where multiple lines of code are run in a single moment. That’s why its so fast!
  1. No database or dependencies
  • A static site does not need a database. It is just codes and media like pictures, videos or sounds. Hugo sites also run without any dependencies on expensive runtimes such as PHP or Python.

Static sites are not suitable to build every type of website. However, Hugo is best to build:

  • blogs,
  • company websites (eCommerce),
  • portfolio websites,
  • documentation websites,
  • website with a single landing page,
  • or sites with thousands of pages.

If you’d like to build any of the above type of website, read on. Comment if you’d like me to share other types of tutorial.

This is the preview of the website we are building in this tutorial:

Preview

Install Hugo

Let us begin by installing Hugo on our computer. You can check out their website for different installation methods or based on your device. For this example, I will download and extract the zip file in a Windows 10 machine.

1. Create the Hugo Directory

Open Windows Explorer and create a new folder in the C:\ directory called Hugo (capital H for Hugo) or C:\Hugo. Inside the Hugo folder, create a file called bin or C:\Hugo\bin

It should look something like this.
It should look something like this.

2. Downloading Hugo

Before downloading, you would need to know if your computer is on a 32-bit or 62-bit operating system at the Device Specifications in the System settings. To do that, open Windows Explorer and right-click on This PC and click on Properties (or you could right-click at Start button and click on System).

Click on Properties
Click on Properties

Mine is 64 bit
Mine is 64 bit

Then, you can proceed to the releases page to download the respective zipped folder into the C:\Hugo\bin created earlier.

Click based on your computer’s operating system
Click based on your computer's operating system

Then proceed to extract hugo.exe , LICENSE and README.md into the C:\Hugo\bin that we created earlier.

The files that should appear in <code>C:\Hugo\bin</code>
The files that should appear in C:\\Hugo\bin

3. Add Hugo to Windows PATH settings

Once you have moved the files, you would need to add Hugo to Windows Path Settings. Go to the Device Specifications where you found your OS type (32-bit or 64-bit) earlier.

right-click at Start button and click on System
right-click at Start button and click on System

Then go to Advanced System Settings
Then go to Advanced System Settings

Click on Environment Variables
Click on Environment Variables

In the User Variables section (upper), double click on Path
In the User Variables section (upper), double click on Path.

Then click on New and type C:\Hugo\bin where hugo.exe is located in your device.

After you&rsquo;re done typing, click Enter.
After you're done typing, click Enter.

Then click OK to exit at every window.

To verify that your installation is correct, open Command Prompt (Click start or Windows button, type cmd and click Enter). When you type hugo help and click enter, an output should appear that starts with

hugo is the main command, used to build your Hugo site.

Hugo is a Fast and Flexible Static Site Generator
built with love by spf13 and friends in Go.

Complete documentation is available at http://gohugo.io/.

When you type path and click Enter, among the path available, you should also see:

C:\Hugo\bin

Creating your Hugo website

I will be using Visual Studio Code, a source code editor by Microsoft for Windows, Linus or MacOS. So if you are not using Windows, don’t worry, you can still follow along as the command and the interface will be similar.

1. Create a folder

Since you have installed Hugo on your PATH settings, you can use it anywere in your Windows device (if you’re having trouble with getting Hugo in your PATH, run the create new hugo site command inside C:\Hugo\bin instead).

I have created a folder called MyBlog in Documents folder.
I have created a folder called MyBlog in Documents folder.

2. Open Source Code Editor

Type cmd in the address bar.
Type cmd in the address bar.

When the command prompt appears, type code . like so, and click Enter:

C:\Documents\MyBlog>code .

Your workspace is now ready in Visual Studio Code.
Your workspace is now ready in Visual Studio Code.

To open Visual Studio Code from Terminal in MacOS, can check out their documentation here.

3. Create new Hugo site

A good thing about Visual Studio Code is that it comes packed with its own Terminal. To open it, click Ctrl + ` simultaneously.

Then type hugo new site myblog in the Terminal (myblog is just a folder name. You can replace it with any name that you like. It does not determine your website name or other values):

C:\Documents\MyBlog>hugo new site myblog

When you click Enter, a few files and folders are created for you in the following manner:

.
├── archetypes
│   └── default.md
├── content
├── data
├── layouts
├── static
├── themes
└── config.toml

Congratulations, your new Hugo site is built! Let’s start this adventure!

Creating your own theme

Since Hugo is a static site generator, you’d need to employ your knowledge of basic HTML, CSS and JS to design how it looks and how it functions.

Here is an introduction to that topic.

For this tutorial, we are diving into the creation of our own customizable theme with the help of Bootstrap 5. If you are more into ready-made themes, there are plethoras of beautiful, open-sourced Hugo themes available at Hugo Themes Collection.

Create a Hugo skeleton theme

Hugo comes with a command to create a skeleton theme which I will use to develop this blog. To do that:

  1. (Change Directory) cd into myblog

    C:\Documents\MyBlog>cd myblog
    
  2. Use the hugo new theme command and name your theme. Let’s name it four

    C:\Documents\MyBlog\myblog>hugo new theme four
    
  3. Hugo will create your new theme files that look like this:

.
├── archetypes
│   └── default.md
├── content
├── data
├── layouts
├── resources
│   └── _gen
│       ├── assets
│       └── images
├── static
├── themes
│   └──  four
│           ├── archetypes
│           │   └── default.md
│           ├── layouts
│           │   ├── _default
│           │   │   ├── baseof.html
│           │   │   ├── list.html
│           │   │   └── single.html
│           │   ├── partials
│           │   │   ├── footer.html
│           │   │   ├── head.html
│           │   │   └── header.html
│           │   ├── 404.html
│           │   └── index.html
│           ├── static
│           │   ├── css
│           │   └── js
│           ├── LICENSE
│           └── theme.toml
└── config.toml

In the layouts folder, notice that only baseof.html comes pre-filled. If you’re familiar with HTML, notice its similarities. But with Golang, it comes with curly brakcets {} codes.

Hugo is designed so that each section is coded separately (as partials) and is combined to create complete html templates throughout your website pages or when you want to. This is similar to Python method of programming where it employs the DRY (Don’t Repeat Yourself) method.

Install Bootstrap

To use Bootstrap 5 in our website, let’s visit their website for the documentation.

  1. Create new folder in your four themes folder called assets. Then create two folders called css and js in the assets folder. It should look like so:
four
    └── assets
            ├── css
            └── js
  1. Download the Compiled CSS and JS files from their website. Extract bootstrap.min.css into css folder and create a new file called style.css for any custom changes that we might have. Similarly, extract bootstrap.bundle.min.js into the js folder created earlier. The structure should like this so far:
four
    └── assets
            ├── css
            │   ├── bootstrap.bundle.min.css
            │   └── style.css
            └── js
                └── bootstrap.bundle.min.js

Add the following code inside style.css:

section {
  min-height: 50vh;
}

#heroFour .carousel-item > img {
  height: 70vh;
  object-fit: cover;
  object-position: 50% 50%;
}
  1. baseof.html
  • Beside the html tag, add lang=“en” to follow Bootstrap’s Starter Template.
  • We are using the row and columns functions to create a responsive sidebar. Add a partial called sidebar.html (create a new file called sidebar.html in the partial folder).
  • Add also a partial called scripts.html (create a new file called scripts.html in the partial folder too).
  • Your baseof.html should look like this:
<!DOCTYPE html>
<html lang="en">
  {{- partial "head.html" . -}}
  <body>
    {{- partial "header.html" . -}}
    <div class="container p-3">
      <div class="row">
        <div class="col-sm-7">{{- block "main" . }}{{- end }}</div>
        <div class="col-sm-5">{{- partial "sidebar.html" . -}}</div>
      </div>
    </div>
    {{- partial "footer.html" . -}} {{- partial "scripts.html" . -}}
  </body>
</html>
  1. For the partial scripts.html, add the following and click save:
{{ $bootstrapbundle := resources.Get "js/bootstrap.bundle.min.js" }}
<script src="{{ $bootstrapbundle.Permalink }}"></script>
<!-- Add your custom JS below this line -->

$bootstrapbundle is a variable to retrieve the asset file that is inside the assets folder. This method is especially handy if you’d like to use Hugo built-in features such as to minify or fingerprint files stored in assets folder.

head.html

For the partial head.html, add the following dependencies and click save:

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <!-- Generator -->
  {{- hugo.Generator }}

  <!-- Bootstrap CSS -->
  {{ $bootstrap := resources.Get "/css/bootstrap.min.css" }}
  <link rel="stylesheet" href="{{ $bootstrap.Permalink }}" as="style" />

  <!-- Fontawesome Icons -->
  <link
    rel="stylesheet"
    href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
    integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
    crossorigin="anonymous"
    referrerpolicy="no-referrer"
  />

  <!-- Custom CSS -->
  {{ $style := resources.Get "/css/style.css" | minify | fingerprint }}
  <link rel="stylesheet" href="{{ $style.Permalink }}" as="style" />

  <!-- Title -->
  {{ $title := print .Title " | " .Site.Title }} {{ if .IsHome }}{{ $title =
  .Site.Title }}{{ end }}
  <title>{{ $title }}</title>

  <!-- Favicon -->
  <link rel="icon" href="{{- .Site.Params.favicon | default "favicon.ico" |
  absURL -}}" sizes="256x256"> {{ template "_internal/disqus.html" . }} {{
  template "_internal/google_news.html" . }} {{ template
  "_internal/google_analytics.html" . }} {{ template
  "_internal/google_analytics_async.html" . }} {{ template
  "_internal/opengraph.html" . }} {{ template "_internal/schema.html" . }} {{
  template "_internal/twitter_cards.html" . }}
</head>

Variables

As you can see, the $style variable is minified and fingerprinted. Minify will optimize the size of your files and fingerprint will generate random numbers after your style.css to keep your website following the latest updated version.

Site Variables and config.toml

Any variable that begins with .Site such as .Site.Title will be called in the config.toml. This means that you can have a standardized value for your whole website in the config file. You can also use config.yaml or config.json based on your language preference.

Template

I have also added some Hugo ready-made Internal Templates for most common use cases.

header.html

For the partial header.html, add the following and click save:

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="{{ "/" | relURL }}">{{ .Site.Title}}</a>

    <!-- Activated for mobile view -->
    <button
      class="navbar-toggler"
      type="button"
      data-bs-toggle="collapse"
      data-bs-target="#navbarSupportedContent"
      aria-controls="navbarSupportedContent"
      aria-expanded="false"
      aria-label="Toggle navigation"
    >
      <span class="navbar-toggler-icon"></span>
    </button>

    <!-- Menu Start -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
        {{ range .Site.Menus.main }}
        <li class="nav-item">
          {{ $text := print .Name | safeHTML }}
          <a class="nav-link" title="{{ $text }}" href="{{ .URL }}">
            {{ $text }}
          </a>
        </li>
        {{ end }}
      </ul>

      <div class="navbar-nav d-flex flex-row justify-content-around">
        {{ range .Site.Menus.social }}
        {{ $title := print .Title | safeHTML }}
        <a class="nav-link" title="{{ $title }}" href="{{ .URL }}">
          {{ $text := print .Name | safeHTML }}
          <i class="{{ $text }}"></i>
        </a>
        {{ end }}
      </div>
    </div>
    <!-- Menu End -->
  </div>
</nav>

footer.html

For the partial footer.html, add the following and click save:

<footer class="footer text-center bg-dark py-5 text-light">
  <!-- Change YourName to any name of your choice -->
  <p>Copyright &copy; {{ now.Format "2006"}} YourName</p>
  <p>
    Designed by
    <a class="link-secondary" href="https://www.heksagon.net/">Heksagon</a> with
    love ❤
  </p>
  <p>
    <a class="link-secondary" href="#">Back to top</a>
  </p>
</footer>

sidebar.html

For the partial sidebar.html, add the following and click save:

<div class="card mx-3 my-5 p-3">
  <h2>Sidebar</h2>
  <p>
    Add any function that you like in the <code>sidebar.html</code> partial.
  </p>
</div>

404.html

404 page is a page where visitors are redirected to when the url after your domain name is invalid. In order for it to load in that logic, there needs to be some customizations for different servers and automatic in others. Hugo also provides some guidelines for websites serving under these environments:

  • GitHub Pages and GitLab Pages.
  • Apache.
  • Nginx.
  • Amazon AWS S3.
  • Amazon CloudFront.
  • Caddy Server.
  • Netlify.
  • Azure Static website.

If you just want to test it out in live preview, just go to http://localhost:1313/404.html

Outside the partials folder, in the layouts folders, there are two files that were created which are 404.html and index.html. In the 404.html, add the following:

{{ define "main" }}
<section id="main" class="text-center d-flex flex-column justify-content-center">
    <h1>Page Not Found</h1>
    <div>
     <h1><a class="btn btn-lg btn-outline-secondary" href="{{ "/" | relURL }}">Go Home</a></h1>
    </div>
</section>
{{ end }}

metadata.html

index.html which is placed in the root folder of your web server is widely known as the home page. Before we proceed to create the home page, in the partials folder, create a metadata.html to call the date of the blog post. You can also have aditional information such as tags, author name, and many more. But for this tutorial, we will just add the date of post as metadata like so:

{{ $dateTime := .PublishDate.Format "2006-01-02" }} {{ $dateFormat :=
.Site.Params.dateFormat | default "Jan 2, 2006" }}
<i class="far fa-calendar-alt"></i>
<time datetime="{{ $dateTime }}">{{ .PublishDate.Format $dateFormat }}</time>

index.html

The home page. I added a few of Bootstrap’s template to get you started as soon as possible. Edit to showcase your creativity!

<!DOCTYPE html>
<html lang="en">
{{- partial "head.html" . -}}
<body>
{{- partial "header.html" . -}}

<!-- Hero Carousel -->
<section id="heroFour" class="carousel slide" data-bs-ride="carousel">
  <div class="carousel-indicators">
    <button type="button" data-bs-target="#heroFour" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
    <button type="button" data-bs-target="#heroFour" data-bs-slide-to="1" aria-label="Slide 2"></button>
    <button type="button" data-bs-target="#heroFour" data-bs-slide-to="2" aria-label="Slide 3"></button>
  </div>
  <div class="carousel-inner">
    <div class="carousel-item active">
      <img src="{{ "/img/hero1.webp" | relURL }}" class="d-block w-100" alt="...">
      <div class="carousel-caption d-md-block">
        <h1>Life is either a daring adventure or nothing at all</h1>
        <p>Helen Keller</p>
      </div>
    </div>
    <div class="carousel-item">
      <img src="{{ "/img/hero2.webp" | relURL }}" class="d-block w-100" alt="...">
      <div class="carousel-caption d-md-block">
        <h1>Traveling – it leaves you speechless, then turns you into a storyteller</h1>
        <p>Ibn Battuta</p>
      </div>
    </div>
    <div class="carousel-item">
      <img src="{{ "/img/hero3.webp" | relURL }}" class="d-block w-100" alt="...">
      <div class="carousel-caption d-md-block">
        <h1>Life’s a journey not a destination</h1>
        <p>Aerosmith</p>
      </div>
    </div>
  </div>
  <button class="carousel-control-prev" type="button" data-bs-target="#heroFour" data-bs-slide="prev">
    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Previous</span>
  </button>
  <button class="carousel-control-next" type="button" data-bs-target="#heroFour" data-bs-slide="next">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Next</span>
  </button>
</section>

<!-- Our Values -->
<section class="py-5">
  <h2 class="text-center">We are committed</h2>
  <div class="container">
    <div class="row align-items-center">
      {{ range .Site.Params.values }}
      <div class="col-12 col-sm-4 text-center  py-5">
        <i class="{{ .iconclass }} "></i>
        <h2>{{ .title }}</h2>
        <p>{{ .description }}</p>
      </div>
      {{ end }}
    </div>
  </div>
</section>

<!-- Jumbotron -->
<section class="py-5 bg-warning">
  <div class="p-5 mb-4 rounded-3">

    <div class="container-fluid py-5">
      <h1 class="display-5 fw-bold">Custom jumbotron</h1>
      <p class="col-md-8 fs-4">You are able to create this jumbotron, similar to previous versions of Bootstrap.</p>
      <button class="btn btn-primary btn-lg" type="button">Button</button>
    </div>

    <div class="row align-items-md-stretch">

      <div class="col-md-6">
        <div class="h-100 p-5 text-white bg-dark rounded-3">
          <h2>Dark background</h2>
          <p>Swap the background-color utility and add a `.text-*` color utility to mix up the jumbotron look. Then, mix and match with your favourite choice.</p>
          <button class="btn btn-outline-light" type="button">More ...</button>
        </div>
      </div>

      <div class="col-md-6">
        <div class="h-100 p-5 bg-light border rounded-3">
          <h2>Add borders</h2>
          <p>You can also keep it light and add a border for some added definition to the boundaries of your content. </p>
          <button class="btn btn-outline-secondary" type="button">Be Creative</button>
        </div>
      </div>

    </div>
  </div>
</section>

<!-- Blog Section -->
<section class="album py-5 bg-light">
  <div class="text-center py-5">
    <h2 class="fs-1">Blog</h2>
    <p class="fs-4 text-muted">Latest Article</p>
  </div>
  <div class="container">
    <div class="row d-flex justify-content-center">
      {{ range (.Paginator 3).Pages.ByPublishDate.Reverse }}
      <div class="col-sm-4 my-3">
        <div class="card shadow-sm">
          <img class="bd-placeholder-img card-img-top" width="100%" height="225" src="{{ .Params.cover.image }}" style="object-fit: cover;"></img>

          <div class="card-body">
            <h3 class="card-text">{{ .Title }}</h3>
            <p class="card-text">{{ partial "metadata.html" . }}: {{ .Params.description }}</p>
            <div class="d-flex justify-content-between align-items-center">
              <div class="btn-group">
                <a href="{{ .RelPermalink }}">
                <button type="button" class="btn btn-sm btn-outline-secondary"> View</button></a>
              </div>
              <div class="btn-group">
                {{ with .Params.tags }}
                {{ range . }}
                {{ $href := print (absURL "tags/") (urlize .) }}
                <a class="btn btn-sm btn-outline-secondary" href="{{ $href }}">{{ . }}</a>
                {{ end }}
                {{ end }}
              </div>
            </div>
          </div>
        </div>
      </div>
      {{ end }}
    </div>
    <p class="text-end">
      <a href="{{ "/posts/" | relURL }}">More...</a>
    </p>
  </div>
</section>

<!-- Contact Section -->
<section id="contact" class="py-5 bg-warning" style="background-image: url(/img/map-image.png);background-size: contain;background-repeat: no-repeat;">
  <div class="container">
    <div class="row">
      <div class="col-sm-6 d-flex align-items-center">
        <h2>Contact Us</h2>
      </div>
      <div class="col-sm-6">
        <form action="">
          <div class="m-3">
            <label for="exampleFormControlInput1" class="form-label">Email address</label>
            <input type="email" class="form-control" id="exampleFormControlInput1" placeholder="your@email.com">
          </div>
          <div class="m-3">
            <label for="exampleFormControlTextarea1" class="form-label">Your messsage here:</label>
            <textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
          </div>
        </form>
      </div>
    </div>
  </div>
</section>

{{- partial "footer.html" . -}}
{{- partial "scripts.html" . -}}
</body>
</html>

Contact Section

In the contact section, the form here can be replaced with form providers such as Formspree.io or Formsubmit.io so website visitors can contact you. It also possible to code your own PHP function to receive submissions.

config.toml

This file is used to store main information regarding a Hugo website, integrate certain features and call images. All of the value in this file is adjusted based on your website needs. The most important thing is to link with the theme that was created:

theme = "four"

BaseURL is your website domain name, title is your website title, your own Google Analytics ID and your own Disqus Short Name. Here is a short example that I used for this Hugo tutorial:

baseURL = "http://example.org/"
languageCode = "en-us"
title = "Four: Travel Blog"
theme = "four"
googleAnalytics = "UA-PROPERTY_ID/G-MEASUREMENT_ID"
disqusShortname = "yourdiscussshortname"

[params]
description = "Open Graph Description"
images = [ "/img/hero1.webp" ]
title = "Four: Travel Blog"
favicon = "img/world-map-pin.svg"

  [[params.values]]
  iconclass = "fas fa-pen text-primary fa-3x py-3"
  title = "Blog"
  description = "Dedicated to sharing insightful information"

  [[params.values]]
  iconclass = "fas fa-bolt text-primary fa-3x py-3"
  title = "Speed"
  description = "Our priority in getting the job done fast"

  [[params.values]]
  iconclass = "fas fa-comment text-primary fa-3x py-3"
  title = "Chat"
  description = "Keep in touch with us to know more"

[minify]
minifyOutput = true

[[menu.main]]
identifier = "blog"
name = "Posts"
url = "/posts/"
weight = 1

[[menu.main]]
identifier = "Contact"
name = "Contact"
url = "/#contact"
weight = 2

[[menu.main]]
identifier = "tags"
name = "Tags"
url = "/tags"
weight = 3

[[menu.social]]
name = "fab fa-twitter fa-2x link-info"
title = "Twitter"
url = "/"
weight = 1

[[menu.social]]
name = "fab fa-youtube fa-2x link-danger"
title = "Youtube"
url = "/"
weight = 2

Images

Images are one of the most important elements of a website. They make your website colourful and interesting. If not optimized, it can also slow down your website loading speed. Notice that in this example, most of the images used are in webp format.

I wrote a short article regarding optimizing images to WebP format.

In the static folder for the four theme, create a new folder called img to keep images for the config.toml and index.html:

static
    ├── css
    ├── img
    │    ├── hero1.webp
    │    ├── hero2.webp
    │    ├── hero3.webp
    │    ├── map-image.png
    │    └── world-map-pin.svg
    └── js

Hugo Default layout

In the themes layouts folder, there is another folder called _default which stores the basic Hugo layouts:

  1. list.html is the layout template to list all blog posts
  2. single.html is the layout template to list a single post
  3. terms.html is the layout template to list taxonomies. In this examples, only tags taxonomy is used.

Each layout is styled based on suitable Bootstrap classes:

list.html

{{ define "main" }}
<h1>{{ .Title }}</h1>
<div class="row d-flex justify-content-center">
  {{ range .Pages.ByPublishDate.Reverse }}
  <div class="col-lg-6 my-3">
    <div class="card shadow-sm">
      <img class="bd-placeholder-img card-img-top" width="100%" height="225" src="{{ .Params.cover.image }}" style="object-fit: cover;"></img>
      <div class="card-body">
        <h3 class="card-text">{{ .Title }}</h3>
        <p class="card-text">{{ partial "metadata.html" . }}: {{ .Params.description }}</p>
        <div class="d-flex justify-content-between align-items-center">
          <div class="btn-group">
            <a href="{{ .RelPermalink }}">
            <button type="button" class="btn btn-sm btn-outline-secondary"> View</button></a>
          </div>
          <div class="btn-group">
            {{ with .Params.tags }}
            {{ range . }}
            {{ $href := print (absURL "tags/") (urlize .) }}
            <a class="btn btn-sm btn-outline-secondary" href="{{ $href }}">{{ . }}</a>
            {{ end }}
            {{ end }}
          </div>
        </div>
      </div>
    </div>
  </div>
{{ end }}
</div>
{{ end }}

single.html

{{ define "main" }}
<h1>{{ .Title }}</h1>
<img
  class="w-100"
  src="{{ .Params.cover.image }}"
  alt="{{ .Params.cover.alt }}"
/>
<p class="text-center">{{ .Params.cover.caption }}</p>

<div class="row text-center">
  <div class="col">{{ partial "metadata.html" . }}</div>
  <div class="col">
    <div class="btn-group">
      {{ with .Params.tags }} {{ range . }} {{ $href := print (absURL "tags/")
      (urlize .) }}
      <a class="btn btn-sm btn-outline-secondary" href="{{ $href }}">{{ . }}</a>
      {{ end }} {{ end }}
    </div>
  </div>
</div>
<br /><br />
{{ .Content }} {{- partial "disqus.html" . -}} {{ end }}

terms.html

{{- define "main" }}
<section class="row">
  <div class="has-text-centered">
    <h1 class="fs-1">{{ .Title }}</h1>
    <p>{{ .Description }}</p>
  </div>
  <div class="col">
    <div class="container">
      <ul class="list-group">
        {{- $type := .Type }} {{- range $key, $value := .Data.Terms.Alphabetical
        }} {{- $name := .Name }} {{- $count := .Count }} {{- with $.Site.GetPage
        (printf "/%s/%s" $type $name) }}
        <li class="list-group-item btn btn-sm btn-outline-secondary">
          <a
            class="stretched-link text-decoration-none link-secondary fs-4"
            href="{{ .Permalink }}"
            >{{ .Name }}
            <sup
              ><strong class=""><sup>{{ $count }}</sup></strong></sup
            ></a
          >
        </li>
        {{- end }} {{- end }}
      </ul>
    </div>
  </div>
</section>
{{- end }}{{/* end main */ -}}

Comment Section

Notice in single.html, there is a partial that wasn’t created, disqus.html. This partial is already included in the internal templates declared at the head.html. However, Hugo recommends to add the following partial called disqus.html to avoid unwanted discussions with associated Disqus account in the localhost environment:

<div id="disqus_thread"></div>
<script type="text/javascript">
  ;(function () {
    // Don't ever inject Disqus on localhost--it creates unwanted
    // discussions from 'localhost:1313' on your Disqus account...
    if (window.location.hostname == 'localhost') return

    var dsq = document.createElement('script')
    dsq.type = 'text/javascript'
    dsq.async = true
    var disqus_shortname = '{{ .Site.DisqusShortname }}'
    dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'
    ;(
      document.getElementsByTagName('head')[0] ||
      document.getElementsByTagName('body')[0]
    ).appendChild(dsq)
  })()
</script>
<noscript
  >Please enable JavaScript to view the
  <a href="https://disqus.com/?ref_noscript"
    >comments powered by Disqus.</a
  ></noscript
>
<a href="https://disqus.com/" class="dsq-brlink"
  >comments powered by <span class="logo-disqus">Disqus</span></a
>

If you like to use other comment providers as suggested by Hugo, you can

  • remove Disqus internal template in head.html
  • remove the Disqus Shortname is config.toml and
  • change the partial in single.html to a comment provider of choice

Blog Content

Write a blog

This blog project is almost complete. Now run the following in the terminal:

C:\Documents\MyBlog\myblog>hugo new posts/post1.md

This command creates a new posts folder inside content folder with a markdown file called post1.md:

---
title: 'Post1'
date: 2021-04-01T23:11:13Z
draft: true
---

These three variables are called Front Matter and are based on the template in default.md in the archetypes folder. You can adjust your defaults there so that when you run hugo new , your desired template appears. I will just adjust post1.md to be as such:

---
title: 'My First Post'
date: 2021-04-01T23:11:13Z
draft: true
tags: ['Africa', 'adventure']
description: 'This is the first post description'
images: ['/img/post1.webp']
cover:
  image: '/img/post1.webp'
  alt: 'This is a random image from Unsplash'
  caption: 'This is a random image from Unsplash'
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Pellentesque eu tincidunt tortor
aliquam nulla facilisi cras fermentum odio. A erat nam at lectus urna duis. Sed
velit dignissim sodales ut eu sem. Lectus urna duis convallis convallis tellus.
Diam sit amet nisl suscipit adipiscing bibendum est. Sed felis eget velit
aliquet sagittis id consectetur. Vulputate dignissim suspendisse in est ante in
nibh mauris cursus. Morbi quis commodo odio aenean. Mollis nunc sed id semper
risus in hendrerit gravida rutrum. Ac ut consequat semper viverra nam. Hac
habitasse platea dictumst vestibulum rhoncus. Amet porttitor eget dolor morbi
non. Justo eget magna fermentum iaculis eu non. Id eu nisl nunc mi ipsum
faucibus vitae aliquet nec. Aliquam id diam maecenas ultricies. Non sodales
neque sodales ut etiam. Amet massa vitae tortor condimentum lacinia quis. Erat
imperdiet sed euismod nisi porta. Nisl suscipit adipiscing bibendum est
ultricies integer quis auctor. Viverra suspendisse potenti nullam ac. Tincidunt
id aliquet risus feugiat in. Varius quam quisque id diam vel. Egestas erat
imperdiet sed euismod nisi. Scelerisque felis imperdiet proin fermentum leo vel
orci porta non. Ut faucibus pulvinar elementum integer. Fermentum odio eu
feugiat pretium nibh ipsum consequat nisl.

Blog Images

Create a new file in the main static folder called img and save all your images for your blog posts here. This is to segregate your blog content and your theme images for easier editing and website management later on.

.
├── archetypes
│    └── default.md
├── content
├── data
├── layouts
├── static
│    └── img
│         └── post1.webp
├── themes
└── config.toml

Live Preview

Finally, the time to test your site in a web environment. Hugo comes with a built-in live preview capability with Hugo server. Just need to run the following command in the terminal (-D is with drafts enabled to view our drafted First Post):

C:\Documents\MyBlog\myblog>hugo server -D

Go to any web browser and run http://localhost:1313/. You should be able to see and explore your completed site now. Save after you make any changes to view the change taking place live.

Deploy to a Live Site

After you have made changes and want your website to be accessible from your purchased domain name, here are the steps:

1. Purchase a domain name and hosting

If you plan to monetize your website or establish an authority, having your own domain is the way to go. Here are a few recommended web hosting providers which helps me earn a small commission if you register with these links:

Heksagon's Exclusive Affiliate Link
Quality Web Hosting At A Discounted Offer

For free web hosting for static sites, can check out the following links:

  1. Github Pages
  2. Netlify
  3. 000WebHost
  4. Firebase
  5. Google Cloud

2. draft: false

Change the front matter from draft true to false and check all functions, images and styling works in live preview.

3. Generate Static Site

Once everything is ready for production, run the following command

C:\Documents\MyBlog\myblog>hugo

Hugo will generate a complete static site (HTML, CSS, JS) in the public folder. To make your website live, you’d just need to put the contents of this folder in the root folder of your website.

Conclusion

Congratulations! Your Website is now LIVE in the World Wide Web. If you’d like me to do more tutorials like this, do share in the comment section here. This Hugo tutorial covers only the basic of creating a functional but simple blog using Bootstrap 5.

Check out the preview of the live site that is built in this article:

Preview

We use cookies to improve your experience on our site and to show you relevant advertising.

Read cookie policy

Accept & Close