Introduction
Hugo is a fast, open-source static web site generator. While it’s very flexible and has a lot of documentation, it isn’t always easy to figure out how create a simple site. This simple step-by-step guide leads you through installing Hugo and creating a blog oriented site with most of the basic features for publishing your content.
This guide is opinionated and prioritizes certain features and functionality and while ignoring others. The features I think are most important for a simple, blog-oriented web site are:
- Homepage which includes content summaries
- Paginated navigation
- Page tags with automatically generated tag index pages
- RSS/Atom feed
- Theme in its own directory separate from content
This guide will show you how to incrementally build all of this functionality with Hugo and provide a companion repository with working source code.
I won’t be covering fancy styles or typography, but you’ll be able to apply whatever styling you want to the simple templates we build. You will need to know how to use your favorite text editor and how to get around on the command line.
Installation
Hugo has excellent installation instructions for your platform. I use Linux and find it easiest to download prebuilt binaries from the GitHub latest release page. I recommend installing the so-called extended version.
Whatever method you use, you should be able to run the hugo command:
$ hugo version
hugo v0.147.9-29bdbde19c288d190e889294a862103c6efb70bf+extended linux/amd64 BuildDate=2025-06-23T08:22:20Z VendorInfo=gohugoio
Note: file paths and commands are shown using Linux format /, modify for Windows if needed \.
Create New Blog and Theme
Generate a skeleton site:
$ hugo new site opinionated-blog
$ cd opinionated-blog
The generated file tree should look like:
$ tree
.
├── archetypes
│ └── default.md
├── assets
├── content
├── data
├── hugo.toml
├── i18n
├── layouts
├── static
└── themes
You could place all your templates in the layout directory, but I think it’s better to generate a new theme in the
theme directory. This keeps the content and the style separate and you can version the theme in its own Git repo.
$ hugo new theme opinionated-theme
Hugo will generate a new theme that looks like:
themes
└── opinionated-theme
├── archetypes
│ └── default.md
├── assets
│ ├── css
│ │ └── main.css
│ └── js
│ └── main.js
├── content
│ ├── _index.md
│ └── posts
│ ├── _index.md
│ ├── post-1.md
│ ├── post-2.md
│ └── post-3
│ ├── bryce-canyon.jpg
│ └── index.md
├── data
├── hugo.toml
├── i18n
├── layouts
│ ├── _default
│ │ ├── baseof.html
│ │ ├── home.html
│ │ ├── list.html
│ │ └── single.html
│ └── partials
│ ├── footer.html
│ ├── head
│ │ ├── css.html
│ │ └── js.html
│ ├── header.html
│ ├── head.html
│ ├── menu.html
│ └── terms.html
├── LICENSE
├── README.md
├── static
│ └── favicon.ico
└── theme.toml
Open up hugo.toml in your favorite editor. Change the title and and add the theme we created to the configuration:
title = 'Opinionated Blog'
theme = 'opinionated-theme'
Simplify Theme
The default theme we created with $ hugo new theme ... has a lot of interesting features that you could use as the
base of a theme, but we are going to simplify things in order to focus on creating basic functionality.
The first thing we want to remove is the content included with the theme. Our content will be located under the
opinionated-blog/content/ directory. Go ahead and delete all of the content in the themes/opinionated-theme/content/
directory.
$ rm -rf themes/opinionated-theme/content/
Let’s also delete the generated templates, since we are going to create them from scratch. They are located in the
layouts directory. One way is using the find command.
find themes/opinionated-theme/layouts/ -name *.html -exec rm {} +
Next up, we’ll create the outer shell, or chrome of our simple site.
Base Templates
We are going to create the outer shell of our website using what Hugo calls Base Templates, blocks, and Partials.
Each page will have the following sections:
- Header. Contains the logo and title, and main navigation.
- Navigation. Links to different sections or pages.
- Main Content. The primary content of the page.
- Footer. Contains copyright, contact information, etc.
Using your favorite text editor, create the default base template at
themes/opinionated-theme/layouts/_default/baseof.html. This will be the shell for all of our pages.
<!doctype html>
<html lang="{{ .Site.LanguageCode }}">
<head>
<title>{{ block "title" }}{{ .Site.Title }}{{ end }}</title>
</head>
<body>
<header>
{{ partial "header.html" . }}
</header>
<nav>
{{ partial "nav.html" . }}
</nav>
<main>
{{ block "main" . }}
{{ end }}
</main>
<footer>
{{ partial "footer.html" . }}
</footer>
</body>
</html>
The .Site.LanguageCode variable will be replaced with the languageCode variable in the hugo.toml file.
The Go Templates that start with partial will be replaced with the content of
themes/<THEME>/layouts/partials/<PARTIALNAME>.html. The templates that starts with block will be filled in by other
templates that we will build later on. We’ll build the three partials in the next chapter.