Use modules
Introduction
A module is a packaged combination of components that may include archetypes, assets, content, data, templates, translation tables, and static files. A module may be a theme, a complete project, or a smaller collection of one or more components.
- Modules can be imported in any combination or sequence.
- Module imports are recursive; importing Module A can trigger the import of Module B, and so on.
- Modules can provide configuration files and directories, subject to the constraints described in the merge configuration settings section of the documentation.
- External directories, including those from non-Hugo projects, can be mounted to create a unified file system.
Import
To import a module, first initialize the project itself as a module. For example:
hugo mod init github.com/user/projectThis will generate a go.mod file in the project root.
The module name is a unique identifier rather than a hosting requirement. Using a name like github.com/user/project is a common convention but it does not mean you must use Git or host your code on GitHub. You can use any name you like if you do not plan to have others import your project as a module. For example, you could use a simple name such as my-project when you run the initialization command.
Then define one or more imports in your project configuration. This contrived example imports three modules, each containing custom shortcodes:
module:
imports:
- path: shortcodes-a
- path: /home/user/shortcodes-b
- path: github.com/user/shortcodes-c
[module]
[[module.imports]]
path = 'shortcodes-a'
[[module.imports]]
path = '/home/user/shortcodes-b'
[[module.imports]]
path = 'github.com/user/shortcodes-c'
{
"module": {
"imports": [
{
"path": "shortcodes-a"
},
{
"path": "/home/user/shortcodes-b"
},
{
"path": "github.com/user/shortcodes-c"
}
]
}
}
Import precedence is top-down. For example, if shortcodes-a, shortcodes-b, and shortcodes-c each define an image shortcode, the image shortcode from shortcodes-a will take effect.
If multiple modules contain data files or translation tables with identical paths, the data is deeply merged, following top-down precedence.
When you build your project, Hugo will:
- Download the modules
- Cache them for future use
- Generate a
go.sumfile in the project root
See configuring module imports for details and options.
Update
When you import a module, Hugo creates go.mod and go.sum files in your project root, storing version and checksum data. Clearing the module cache and rebuilding will re-download the originally imported module version, as specified in the go.mod file, ensuring consistent builds. Modules can be updated to other versions as needed.
To update a module to the latest version:
hugo mod get -u github.com/user/shortcodes-cTo update a module to a specific version:
hugo mod get -u github.com/user/shortcodes-c@v0.42.0To update all modules to the latest version:
hugo mod get -uTo recursively update all modules to the latest version:
hugo mod get -u ./...Pin
To pin an import to a specific version, set its version in your project configuration. For example:
module:
imports:
- path: github.com/user/shortcodes-c
version: v1.2.3
[module]
[[module.imports]]
path = 'github.com/user/shortcodes-c'
version = 'v1.2.3'
{
"module": {
"imports": [
{
"path": "github.com/user/shortcodes-c",
"version": "v1.2.3"
}
]
}
}
When you pin an import, Hugo downloads the requested version directly, and the hugo mod get command ignores the import. To change the version, edit your project configuration.
The version can be any version query, including:
- A semantic version such as
v1.2.3 - A version prefix such as
v1.2 - A comparison such as
>=v1.2.0 - The string
latest - A branch name such as
main - A commit hash
Use a semantic version where possible so that Hugo can cache the module instead of downloading it each time you build your project. If the module’s Git tags are not valid semantic versions, such as v8.0, pin a commit hash instead.
For major versions 2 and later, if the module has a go.mod file, the module path must end with the major version, and the version must match it. For example:
module:
imports:
- path: github.com/user/theme/v3
version: v3.0.0
[module]
[[module.imports]]
path = 'github.com/user/theme/v3'
version = 'v3.0.0'
{
"module": {
"imports": [
{
"path": "github.com/user/theme/v3",
"version": "v3.0.0"
}
]
}
}
To list the available versions of a module, run go list -m -versions with the module path:
go list -m -versions github.com/user/theme/v3You can import the same module more than once, pinning each import to a different version and mounting each version to a different location. A common pattern is to leave the current version unpinned so that you can update it with hugo mod get, and to pin older versions. For example, this configuration mounts the current version of a module to the content directory, and version 0.1.0 to the content/v1 directory:
module:
imports:
- path: github.com/user/content
- mounts:
- source: content
target: content/v1
path: github.com/user/content
version: v0.1.0
[module]
[[module.imports]]
path = 'github.com/user/content'
[[module.imports]]
path = 'github.com/user/content'
version = 'v0.1.0'
[[module.imports.mounts]]
source = 'content'
target = 'content/v1'
{
"module": {
"imports": [
{
"path": "github.com/user/content"
},
{
"mounts": [
{
"source": "content",
"target": "content/v1"
}
],
"path": "github.com/user/content",
"version": "v0.1.0"
}
]
}
}
Tidy
To remove unused entries from the go.mod and go.sum files:
hugo mod tidyCache
Hugo caches modules to avoid repeated downloads during builds. By default, these are stored in the modules directory within the cacheDir.
To clean the module cache for the current project:
hugo mod cleanTo clean the module cache for all projects:
hugo mod clean --allFor details on cache location and eviction, see configuring file caches.
Vendor
To vendor (verb) is to include the source code of third-party dependencies within your project’s repository, instead of downloading them from an external package manager when needed.
When you vendor dependencies, you copy them from a temporary cache into a dedicated directory that you commit to your version control system.
Vendoring a module provides the benefits described above and allows for local inspection of its components.
hugo mod vendorThis command creates a _vendor directory containing copies of all imported modules, used in subsequent builds. Note that:
- The
hugo mod vendorcommand can be run from any module tree level. - Modules within the
themesdirectory are not vendored. - The
--ignoreVendorPathsflag allows you to exclude vendored modules matching a glob pattern from specific commands.
Instead of modifying files directly within the _vendor directory, override them by creating a corresponding file with the same relative path in your project’s root.
To remove the vendored modules, delete the _vendor directory.
Replace
For local module development, use a replace directive in go.mod pointing to your local directory:
replace github.com/user/module => /home/user/projects/moduleWith hugo server running, this change will trigger a configuration reload and add the local directory to the watch list. Alternatively, configure replacements by setting the replacements parameter in your project configuration.
Workspace
A workspace is a collection of modules on disk.
Workspaces simplify local development of sites with modules. Create a .work file to define a workspace, and activate it via the workspace configuration setting or the HUGO_MODULE_WORKSPACE environment variable.
A .work file example:
go 1.27
use .
use ../my-hugo-moduleUse the use directive to list module paths, including the main project (.). Start the Hugo server with the workspace enabled:
HUGO_MODULE_WORKSPACE=hugo.work hugo server --ignoreVendorPaths "**"The --ignoreVendorPaths flag, used to ignore vendored dependencies (if applicable), enables live reloading of local edits within the workspace.
Graph
To generate a dependency graph, including vendoring, module replacement, and disabled module information, execute hugo mod graph within the target module directory. For example:
$ hugo mod graph
github.com/bep/my-modular-site github.com/bep/hugotestmods/mymounts@v1.2.0
github.com/bep/my-modular-site github.com/bep/hugotestmods/mypartials@v1.0.7
github.com/bep/hugotestmods/mypartials@v1.0.7 github.com/bep/hugotestmods/myassets@v1.0.4
github.com/bep/hugotestmods/mypartials@v1.0.7 github.com/bep/hugotestmods/myv2@v1.0.0
DISABLED github.com/bep/my-modular-site github.com/spf13/hyde@v0.0.0-20190427180251-e36f5799b396
github.com/bep/my-modular-site github.com/bep/hugo-fresh@v1.0.1
github.com/bep/my-modular-site in-themesdirMounts
Imported modules automatically mount their component directories to Hugo’s unified file system. You can also manually mount any directory, including those from non-Hugo projects, to component directories.
See configuring module mounts for details.
