Table of Contents

This is part two of the Master Hugo Modules series. Be sure you’ve completed part one, Managing Themes as Modules: this article requires that your parent project already be set up as a Hugo module.

What’s a Hugo module for content or assets?

Hugo Modules are the core building blocks in Hugo. A module can be your main project or a smaller module providing one or more of the 7 component types defined in Hugo: static, content, layouts, data, assets, i18n, and archetypes. (Hugo Documentation)

A Hugo content or asset module is an external dependency that provides content or asset resources, but not within your Hugo project’s regular /content or /asset directories. Content and asset modules are ideal for read-only content that doesn’t change often. Standardized typographic style guides, shared brand assets, or icon libraries are great examples. At large organizations with multiple websites, content modules can ensure the uniformity of legally-required text like privacy policies, terms of service, copyright, or licensing information.

Why use content or asset modules?

Modules improve developer and editorial workflow. In the past, I’ve copy-and-pasted a typographic style guide from a master repository to each project that needed it… but that’s chunky and error-prone. Moreover, improvements aren’t propagated back upstream without extra effort. But the read-only nature of a content module pushes ongoing improvement upstream: it improves the quality of a library for all projects, by collecting changes as it is implemented in different projects.

This is especially valuable for teams managing multiple sites: consider agencies working with different organizations, or a company working with different brand sites. A well-managed library helps ensure uniformity and consistency.

How to set up a Hugo content module

Create and configure the module

Creating a content or asset module is easy. First, create your repository, and add the content that you wish to share. You can structure this as you like. It’s not required, but consider using the Hugo component /asset and /content directories to keep things orderly.

Once your repo is ready for sharing, you’ll need to initialize it as a Hugo module:

hugo mod init <path-and-repo-name>

Pay attention to this name: the parent projects that will consume this module uses the module name. After hugo mod init, you’ll see a go.modin the project root. Commit everything, then turn to the parent project that will use this module.

Add the content module to the parent project

To use a content module, you’ll update your parent project’s config.toml. You’ll indicate where the module is located, the source directory you want to use, and the “virtual” mount location within your own project. This is described as a file mount: it’s as if this module were located at the target path specified.

For a test, I created a typographic style guide module. It can be mounted to a parent Hugo project like so:

        path = ""
        source = "content/style-guides"
        target = "content/style-guides"

Hugo can pick up this module in a few ways. If you run your standard hugo server and the module isn’t yet registered, it will be — downloaded locally, with your go.mod file updated accordingly. If you want to test paths first,hugo mod get will explicitly pull the module. It’s always a fine practice to see the external dependencies: hugo mod vendor adds them as overrides to the _vendor folder, where you can see the markdown content, treated as if it were in your parent project’s /content/style-guides/ folder.

Add an asset module to a Hugo project

Bjørn Erik Pedersen has made the MIT-licensed HeroIcons available as a Hugo asset module. Add the external module to the parent Hugo project:

path = ""

And now, in your templates, you can call any of the assets as if they were at /assets/svg/heroicons/:

{{ $icon := resources.Get "svg/heroicons/outline/search.svg" }}
{{ $icon.Content | safeHTML }}

Note in this case, I didn’t explicitly declare the file mount source and targets. Instead, they were declared in the module’s own config.toml.

What about headless CMSes?

Git-based headless CMSes like Forestry and NetlifyCMS won’t see content or asset modules, and so, won’t present them as available to the site editor. Just as well, since these are read-only from the perspective of the parent project. A note in the documentation (“How do I get to…”) can help clear up this fantom content.

Up next in the Mastering Hugo Modules series

Folding content and asset modules into your project workflow will help for any projects that have shared assets: brand style guides, logo assets, and more are now readily and consistently available. The next articles will consider private repos — not everything can be public after all — as well as module development time and modules and functions. Feel free to drop me a note on Twitter if you have ideas or questions.