I use Hugo for this site. I migrated to Hugo from WordPress a year ago. Hugo is a static site generator written in Go. Hugo project has frequent releases, so I usually update the version once in a few months. This involves reading all the release notes and making any changes to the theme if required. The theme changes are required rarely though.
While going through the release notes of v0.56.0, I noticed a new feature called Hugo Modules. It uses Go Modules underneath to manage the modules. I wanted to try out that feature but never managed to spend time on it. Finally, while working on PythonPune’s website, I tried Hugo Modules. It is awesome! Let’s see how you can use it for themes and other stuff like shortcodes, layouts and much more.
In a programming language we have dependency management tools to manage the libraries / packages. Hugo Modules serves a similar purpose for a Hugo site. The concept of modules comes from the Go programming language as Hugo Modules in actually powered by the Go Modules.
A static website can have dependencies like themes, some files like CSS or JavaScript and so on. We will take a look at more use cases later.
A Hugo module is a directory which has subdirectories like layouts, static, data etc. These subdirectories hold different files of a Hugo site. Website’s root source directory as well as a theme is also a module.
These subdirectories are referred to as mounts in Hugo modules. content, static, layouts, data, assets, i18n and archetypes are the supported mounts.
Very basic use case of Hugo Modules is adding themes to the site. We don’t have to use git submodules or copy the theme files. This also enables versioning of the theme and pinning it to specific commit.
Same with the files we copy over for shortcodes, static files etc. We just have to add the module and that’s it, Hugo takes care of downloading the files and using them. This simplifies the automated deployment workflows.
With the ability to use only a specific directory or a file from a module, Hugo modules opens up the door to a lot of possibilities and combinations.
The first and one time step is to initialize the site as a
module. This can be done with hugo mod init <module path>
.
To add a module as a dependency of a site, we have to run the command
hugo mod get <module path>
. Then add an entry for the module in site
configuration (in a config.yaml or config.toml file).
The <module path>
is required to be a valid Go module path like
github.com/bep/docuapi
. For local modules we don’t have to run the
get
command. We just need to keep them in the themesDir
(default
is themes
) and add an entry for them.
The Go module path of the dependencies should resolve1 to a valid VCS repository like Git. It can be hosted anywhere like GitLab, GitHub etc. It may or may not have a go.mod file in it. Local Hugo modules don’t need to have a go.mod file if they don’t have any dependencies.
If your site’s source code is not pushed to a public VCS repository, the Go module path for the site can be a single word as well. Note that if the module name does not resolve to a VCS repository, it won’t be possible for others to use the site as a dependency.
Let’s take a look at some examples of site configurations using Hugo modules.
A major dependency of a site is a theme. We will add the
github.com/tummychow/lanyon-hugo
theme to our sample-site
.
To initialize the site as a module and add the theme we run the following commands from the site’s root directory.
$ hugo mod init geeksocket.in/sample-site
go: creating new go.mod: module geeksocket.in/sample-site
$ hugo mod get github.com/tummychow/lanyon-hugo@master
go: downloading github.com/tummychow/lanyon-hugo v0.0.0-20200329195303-72aefc1d4844
go: github.com/tummychow/lanyon-hugo master => v0.0.0-20200329195303-72aefc1d4844
Add a new section in the configuration file,
# config.yaml
title: "My New Hugo Site"
# …
module:
imports:
- path: github.com/tummychow/lanyon-hugo
It is recommended to remove the theme
option from the site
configuration when using modules2.
Let’s add a layout file to our site which is a Markdown render
hook. The
module github.com/bep/portable-hugo-links
is a complete site. We
will just use a specific file from it.
$ hugo mod get github.com/bep/portable-hugo-links@latest
go: downloading github.com/bep/portable-hugo-links v0.5.3
go: github.com/bep/portable-hugo-links latest => v0.5.3
The site configuration,
module:
imports:
# …
- path: github.com/bep/portable-hugo-links
mounts:
- source: layouts/_default/_markup/render-link.html
target: layouts/_default/_markup/render-link.html
The above import specific mount section takes the render-link.html
from the module and makes it available at the same path in our site.
We can mount any other files from our site’s directory on a different path within our site.
module:
# …
mounts:
- source: README.md
target: content/about/index.md
This will create a /about
page using README.md
file which is in
the site root.
We will add jQuery (jquery.js
) to our site at static/js/jquery.js
.
$ hugo mod get github.com/jquery/jquery-dist@3.5.1
go: downloading github.com/jquery/jquery-dist v0.0.0-20200504225046-4c0e4becb826
go: github.com/jquery/jquery-dist 3.5.1 => v0.0.0-20200504225046-4c0e4becb826
The site configuration,
module:
imports:
# …
- path: github.com/jquery/jquery-dist
mounts:
- source: dist/jquery.js
target: static/js/jquery.js
With this change, /js/jquery.js
will be available on the site.
It is possible that a same file is present in two modules. In such cases, the file is taken from the module which appears first in the list.
Consider the following site configuration. Both gkskt-blogstarters
and blogstarters
have the file archetypes/first-post.md
.
module:
imports:
- path: gkskt-blogstarters
- path: blogstarters
The first-post.md
is actually taken from gkskt-blogstarters
in
above case. This way we can combine multiple themes as well.
When making changes to a theme or module, we often want to test it with our site. The replace directive of Go Modules can be used.
We can add a line in the go.mod
file like,
replace github.com/tummychow/lanyon-hugo => /home/bhavin/src/blog/lanyon-hugo
The filesystem path on the right side of =>
needs to have a go.mod
file in it. It can be created with go mod init
.
The same thing can be achieved with the module configuration option
replacements
and the environment variable HUGO_MODULE_REPLACEMENTS
.
Hugo uses /tmp
directory to cache the downloaded modules. This may
get cleaned on system reboot. To avoid this, it can be set to some
other directory with the help of caches
option
in site configuration. Take a look at this
comment
by bep for more information.
hugo mod get
command has different ways to pull a specific
version of a module. You might have noticed some of them in above
examples. Checkout the go get
section from Go Modules
Reference.We can use custom domains which resolve to a hosted
repository. This works with the help of the HTML tag <meta name="go-import" …>
. Read more about it in the Remote import
paths section
of Go documentation. ↩︎
The theme
option is kept for backward compatibility. Take a
look at this
comment
from bep. ↩︎
Comments are not enabled on this site. The old comments might still be displayed. You can reply on one of the platforms listed in ‘Posted on’ list, or email me.