22.3 Mounting Module Paths to Your Project Structure
Right, so you’ve got a Hugo module imported. Great. It’s sitting there in your go.mod file, a neat little line of code promising functionality. But Hugo, in its infinite wisdom, doesn’t just blindly copy the entire contents of that remote repository into your themes or layouts directory. That would be chaos. Instead, it gives you a surgical tool: the mount. This is how you tell Hugo, “See that specific folder inside the module? Yeah, pretend it’s right here in my project, and use it exactly like my local files.” It’s a symlink on steroids, managed by Go.
Think of your module as a vast, multi-room warehouse. You don’t want to move the whole warehouse into your house; you just need the contents of a specific bin in aisle 3. Mounting is you giving Hugo the precise coordinates to that bin.
You do all of this in your config.yaml (or toml, or json—I don’t judge, but YAML is clearer for this). Under the module key, you define an imports path, and for each module you import, you list its mounts.
The Anatomy of a Mount
A mount directive is brutally simple. It needs just two pieces of information, a source and a target. The source is the path inside the module you want to pull from. The target is the path inside your site where you want that content to logically reside. Hugo then seamlessly merges them, with your local files taking precedence (which is what you want).
Let’s say you’re using a module for a photo gallery component, and its repository structure looks like this:
github.com/themaker/a-gallery-module/
├── layouts/
│ └── shortcodes/
│ └── gallery.html
└── assets/
└── css/
└── gallery.css
You want the shortcode locally, but you want to override the CSS with your own tweaks. Here’s how you’d mount it:
# config.yaml
module:
imports:
- path: github.com/themaker/a-gallery-module
mounts:
- source: layouts/shortcodes
target: layouts/shortcodes
- source: assets/css
target: assets/css/gallery
Notice the second mount. I’m mounting the module’s assets/css directory to a subdirectory within my local assets/css called gallery. This is a brilliant trick. It means the module’s gallery.css will be available at /assets/css/gallery/gallery.css, but it won’t conflict with any other local CSS files. I can then choose to leave it as-is, or create my own assets/css/gallery.css which will override it because my local files have higher priority. This is dependency management 101.
Why This Beats the Old Way
If you’re coming from the world of just shoving a whole theme into your themes folder, this might seem like extra work. It is. And it’s worth every second. This approach is declarative. Your config file explicitly states what pieces you’re using from where. Six months from now, when you’ve forgotten about this gallery module, you can look at your config and instantly understand your project’s structure. It also means you can mix and match from multiple modules without them trampling all over each other. You’re the architect, not a janitor cleaning up after a theme party.
The Gotchas: Where This Gets Icky
The
staticDirectory Quirk: This is the big one, and it’s a classic Hugo “character quirk.” For legacy reasons, the top-levelstaticdirectory in a module is automatically mounted to your site’sassetsdirectory. Notstatic, butassets. I know. It’s absurd. It’s the one automatic mount that exists, and it breaks the mental model. If your module has astaticfolder, its contents will be available under/assets. To avoid conflicts, you must use theignoreImportsmount to block this behavior if you don’t want it.mounts: - source: static target: assets ignoreImports: true # "No, Hugo, I do NOT want that."Non-Standard Directories: Want to mount something that isn’t one of Hugo’s canonical directories (
layouts,assets,archetypes,data,i18n,static)? You can, but you have to explicitly declare it as a resource mount usingtype: resource. This is powerful but obscure.mounts: - source: some/custom/data target: data/custom type: resourcePrecedence is Your Best Friend: Remember the merging order: Files in your project root have the highest priority, then mounts are merged in the order they are defined. This means you can carefully orchestrate which module overrides another. It’s not a common need, but when you need it, it’s a lifesaver.
The power here is immense. You’re not just installing a theme; you’re composing your site from a set of discrete, versioned components. You can take a header from one module, a footer from another, and your own content from right here. It’s the difference between buying a pre-built desk and building your own from perfectly crafted parts. It’s more work upfront, but the end result is uniquely, and reliably, yours.