Right, let’s get this party started. You’ve decided to use Hugo Modules, which means you’ve graduated past just dropping themes into a themes folder. Good for you. This is the modern, sane way to manage dependencies in Hugo, and it’s all built on top of Go’s own dependency management system, go mod. Don’t worry if you don’t know Go; you’re about to learn just enough of its tooling to be dangerous in the best way possible.

The first thing you need to wrap your head around is that your Hugo project is about to become a Go module. This doesn’t mean you have to write Go code. It just means we’re hijacking Go’s brilliantly simple and robust system for versioning and dependency management. It’s like using a Ferrari to get your groceries—overkill? Maybe. Effective? Absolutely.

The Non-Negotiable: Install Go

I know, I know. “But it’s a Hugo site!” Trust me on this. You need the go CLI tool installed and available in your PATH. Hugo’s module system literally calls out to the Go toolchain under the hood to do the heavy lifting. If you try to run hugo mod commands without Go installed, you’ll get a error that’s about as helpful as a screen door on a submarine. Check it’s installed:

go version
# Should return something like: go version go1.21.0 linux/amd64

If that didn’t work, stop here and go install Go. I’ll wait.

The Heart of the Operation: go.mod

This unassuming little file is the manifest for your entire project’s dependencies. It’s where you declare what you need and, crucially, which version you need. When you run hugo mod init, Hugo will create this file for you, defining your project as a new module.

The command is straightforward. Navigate to the root of your Hugo project (you know, where your config.toml/config.yaml lives) and run:

hugo mod init github.com/your-username/your-project-repo-name

“Why that long GitHub path?” I hear you cry. Because Go modules are designed for a world of distributed version control. This module path is a unique identifier. It doesn’t have to be a real URL that resolves, but if you ever plan to publish your site as a module for others to use (like a theme), it absolutely should be. If this is just for your own personal use, you can technically use any unique name you want, like example.com/my-awesome-site, but just using the GitHub path is the best practice. It prevents existential dread later.

After you run that command, you’ll see a new go.mod file waiting for you.

// go.mod
module github.com/your-username/your-project-repo-name

go 1.21

// (We'll add requirements (dependencies) in the next section)

See? Not scary. The module directive declares your project’s path, and the go directive specifies the minimum version of Go your module is supposed to work with. Hugo sets a sensible default here; just roll with it.

The Obvious But Critical Pitfall

You must run this command in the correct directory. Your project root. If you do it somewhere else, you’ll end up with a go.mod file that’s utterly disconnected from your Hugo project, and nothing will work. Hugo will look for this file to understand your project’s base. If it’s in the wrong place, it’s like trying to read a map for Berlin when you’re in Bangkok—profoundly unhelpful.

What About Existing go.mod Files?

If you already have a go.mod file in your project root (maybe you’re a Gopher dabbling in Hugo), just run hugo mod init without any arguments. Hugo is smart enough to see the existing file and will just initialize the module system for itself, leaving your precious go.mod intact. It’s a good citizen.

Now, with your go.mod file created, you’ve laid the foundation. You’ve told Hugo, “Yes, I consent to using your modern dependency management system.” You’ve unlocked the ability to pull in themes, components, or even just bits of config from anywhere on the internet, pinned to specific versions, all with a few lines in a config file. This is where the real fun begins.