Right, let’s talk about module.imports. This is where you stop pretending your Hugo site is a lone wolf and start admitting it needs friends. Or, more accurately, libraries. This configuration block is your explicit declaration of dependency, your way of telling Hugo, “Hey, go get this other code from over there so we can use it here.”

Forget the old theme setting; that was a blunt instrument. module.imports is a scalpel. It’s how you pull in other Hugo Modules—which could be full-blown themes, just a few templates, or a single archetype file—with surgical precision. The magic behind this is Go Modules, which Hugo leans on. This means you get proper versioning, and you don’t have to nervously copy and paste layouts/ directories anymore like some digital hoarder.

Here’s the basic incantation. You slap this in your hugo.toml (or yaml, or json—I don’t judge, but TOML is clearly the winner for readability).

[[module.imports]]
path = "github.com/gohugoio/hugo-test-module1"

That’s it. The next time you run hugo mod get -u or just hugo server, Hugo will reach out to the vastness of the internet, find that repository, and download it into your ~/go/pkg/mod cache (or your project’s modules folder if you’re using a go.work file for local development). It will then make its goodies available to your site as if they were always part of your project structure.

The path is (almost) everything

The path directive isn’t just a suggestion; it’s a URL to the repository. For public GitHub repos, you can usually just use the repo path as shown. For private repos or other hosts, you might need to specify a full URL. Hugo’s smart, but it’s not a mind reader. Mostly.

Why you absolutely need a version

Leaving off a version is like ordering “a pizza” and being surprised when you get a margherita instead of the supreme with extra everything. You must be specific. This is the single biggest “oops” moment for people starting with modules.

[[module.imports]]
path = "github.com/gohugoio/hugo-test-module1"
version = "v1.0.4"

You can use semantic versioning tags (v1.2.3), branch names (main—though I beg you, don’t do this for production), or even a specific commit hash (6b86d273). Using a tag is almost always the right answer. It’s stable, predictable, and won’t change underneath you while you sleep.

Ignoring the stuff you don’t want

Here’s a feature so good it feels like a hack. Most themes or modules come with a full project structure: assets/, content/, static/, the whole nine yards. But what if you only want their fancy layouts/partials/ and wish to politely ignore their content/ directory, which is full of their demo posts? You use ignoreConfig and imports. This is Hugo Modules’ killer feature.

[[module.imports]]
path = "github.com/someone/awesome-theme"
version = "v2.3.1"
ignoreConfig = true
[[module.imports.mounts]]
source = "layouts"
target = "layouts"
[[module.imports.mounts]]
source = "assets"
target = "assets"

See that? We’re telling Hugo: “Don’t use any of the config from this module (ignoreConfig = true), and only mount these two specific directories into our site.” The module’s content/, static/, and archetypes/ are left entirely out of the picture. It’s the difference between moving in with a roommate and them taking over your entire apartment versus them just renting the spare bedroom. This is how you avoid getting “theme-owned.”

When things go sideways: the cleanup command

You will, at some point, bork your module setup. The cache will get corrupted, or you’ll try something weird, and Hugo will just look at you blankly. This is when you break out the big guns:

hugo mod clean

This nukes the local cache for your project and forces a fresh download of everything on the next run. It solves a surprising number of bizarre, unexplainable errors. Think of it as turning it off and on again, but for Go modules.

The module.imports system is Hugo finally growing up and adopting proper, modern dependency management. It’s a bit more upfront work than the old way, but the control and stability it gives you are worth their weight in gold. Or, you know, worth the bytes on your disk.