Hugo Modules & Theme Development â Complete Guide
In this tutorial, you'll learn about Hugo Modules & Theme Development. We cover key concepts, practical examples, and best practices.
Hugo modules provide a built-in dependency management system for themes, components, and shared assets â replacing Git submodules with Go-based module resolution for cleaner, more maintainable static site projects.
What You'll Learn
Why It Matters
Managing theme dependencies with Git submodules creates bloated repositories, merge conflicts, and versioning headaches. Hugo Modules solve these problems with semantic versioning, proxy caching, and automatic dependency resolution â all built into Hugo's Go-based architecture. Understanding modules lets you compose sites from reusable components, share themes across projects, and keep your content separate from presentation logic.
Real-World Use
A documentation team maintains a shared theme across five product documentation sites using Hugo Modules, pushing updates to a single theme repository. An agency builds a component library of reusable shortcodes and partials distributed as modules. A developer vendors third-party themes with exact version pinning for reproducible builds.
Module Architecture
flowchart TD A[Site Repository] --> B[Theme Module] A --> C[Component Module] A --> D[Asset Module] B --> E[GitHub Theme Repo] C --> F[Shared Shortcodes] D --> G[SCSS/JS Library] E --> H[Go Module Proxy] F --> H G --> H H --> I[Build Cache] I --> J[Hugo Build] style A fill:#f90,color:#fff style H fill:#09f,color:#fff
Prerequisites: Familiarity with Hugo templates, YAML configuration, and basic Git operations. Hugo Modules require Hugo 0.56+ (0.128+ recommended).
Understanding Hugo Modules
What Is a Module?
A Hugo module is any directory containing a go.mod file and Hugo-compatible content, layouts, assets, or i18n files. Modules can provide:
| Module Type | What It Contains | Example |
|---|---|---|
| Theme | Layouts, templates, styles | hextra, docsy |
| Component | Shortcodes, partials, functions | social-share, toc-generator |
| Asset | SCSS, JS, images | bootstrap-scss, tailwind-components |
| Content | Default pages, archetypes | blog-starter, landing-page |
How Modules Differ from Submodules
| Feature | Git Submodules | Hugo Modules |
|---|---|---|
| Versioning | Git refs only | Semantic versioning (v1.2.3) |
| Dependency resolution | Manual | Automatic, transitive |
| Proxy caching | No | Go module proxy |
| Vendoring | Manual git clone |
hugo mod vendor |
| Overrides | Difficult | replace directives |
| Updates | Manual git commands | hugo mod get -u |
Setting Up Hugo Modules
1. Initialize a Module
# Initialize the module system in your site
hugo mod init github.com/yourusername/your-site
Expected behavior: Hugo creates a go.mod file in the site root. This file tracks all module dependencies and their versions.
2. Add a Theme Module
# hugo.toml â Using a theme as a module
baseURL = "https://example.com"
theme = "hextra"
[module]
[[module.imports]]
path = "github.com/imfing/hextra"
Or in the newer Hugo config format:
# hugo.yaml
baseURL: "https://example.com"
theme: hextra
module:
imports:
- path: github.com/imfing/hextra
Expected behavior: Hugo downloads the hextra module and all its dependencies. The theme is available immediately without manual cloning. Running hugo mod tidy cleans up unused dependencies.
3. Creating a Custom Module
Create a directory with the module structure:
my-module/
âââ go.mod # module declaration
âââ layouts/ # Go templates
â âââ _default/
â âââ shortcodes/
âââ assets/ # SCSS, JS
âââ i18n/ # Translations
âââ data/ # Data files
Initialize it:
cd my-module
hugo mod init github.com/yourusername/my-module
4. Using Local Modules for Development
# hugo.toml â Local development overrides
[module]
[[module.imports]]
path = "github.com/yourusername/my-module"
[[module.mounts]]
source = "my-module"
target = "github.com/yourusername/my-module"
Expected behavior: During development, Hugo uses the local filesystem version of my-module. For production, the mount is removed and Hugo fetches the published version from the module proxy.
Theme Development with Modules
Component-Based Architecture
Organize your theme as independent components:
my-theme/
âââ go.mod
âââ layouts/
â âââ _default/
â â âââ baseof.html
â â âââ single.html
â â âââ list.html
â âââ partials/
â â âââ head.html
â â âââ header.html
â â âââ footer.html
â â âââ sidebar.html
â âââ shortcodes/
â âââ card.html
â âââ alert.html
â âââ figure.html
âââ assets/
â âââ scss/
â â âââ main.scss
â â âââ variables.scss
â âââ js/
â âââ main.js
âââ theme.toml
Overriding Module Templates
Hugo's lookup order prioritizes site-level templates over module templates. To override a partial from a module:
{{/* layouts/partials/header.html â Site-level override */}}
{{/* Hugo looks here first, then in module directories */}}
<header class="site-header">
<nav>{{ partial "nav.html" . }}</nav>
</header>
Composing Multiple Modules
# hugo.toml â Multiple module imports
[module]
[[module.imports]]
path = "github.com/yourusername/theme-core"
[[module.imports]]
path = "github.com/yourusername/social-share"
[[module.imports]]
path = "github.com/yourusername/analytics-component"
[[module.imports]]
path = "github.com/twbs/bootstrap"
disable = false
Expected behavior: Hugo merges all imported modules following the lookup order. The theme-core provides base templates, social-share adds share buttons, and analytics-component inserts tracking snippets.
Version Management
Pinning Versions
# Pin to a specific version
hugo mod get github.com/imfing/hextra@v0.12.3
# Update to latest patch
hugo mod get -u github.com/imfing/hextra
# Update all modules
hugo mod get -u ./...
Vendoring Dependencies
# Copy all module dependencies into your repository
hugo mod vendor
Expected behavior: hugo mod vendor creates a _vendor/ directory containing all module source files. This ensures reproducible builds even if the upstream module becomes unavailable. Run hugo --ignoreVendor to temporarily ignore vendored modules.
Common Errors
1. Module Not Found
If Hugo cannot resolve a module path, ensure the go.mod file at the module root exists and the repository URL is correct. Run hugo mod tidy to update the go.sum file and clean up unused entries.
2. Template Lookup Order Confusion
When multiple modules provide the same template, Hugo uses a strict lookup order: site layouts first, then module imports in declaration order. If the wrong template is rendering, check the import order in your hugo.toml.
3. Missing or Incompatible Module Version
A module update may introduce breaking changes. Pin to a known working version with hugo mod get modulename@v1.2.3. Use hugo mod graph to visualize all dependency relationships.
4. Asset Path Conflicts
Two modules providing the same asset path (e.g., assets/scss/main.scss) cause unpredictable results. Namespace your assets with a prefix: assets/scss/modulename/main.scss and import them explicitly.
5. Module Proxy Connection Issues
Corporate or restricted networks may block the Go module proxy. Configure a custom proxy or disable it:
# Use direct connections instead of proxy
export GOPROXY=direct
# Or use a corporate proxy
export GOPROXY=https://corporate-proxy.example.com,direct
Practice Questions
1. What command initializes Hugo Modules in a project?
hugo mod init <module-path> creates the go.mod file and enables module-based dependency management.
2. How do you override a template from an imported module?
Place a template with the same path in your site's layouts/ directory. Hugo's lookup order checks site-level templates before module templates.
3. What is the purpose of vendoring modules?
Vendoring copies all module source files into the _vendor/ directory, ensuring reproducible builds when the module proxy or source repositories are unavailable.
4. How do you update a single module to its latest version?
Run hugo mod get -u github.com/username/modulename to update a specific module while leaving others unchanged.
5. Challenge: Create a Hugo module that provides a custom figure shortcode with lazy loading, WebP sources, and a caption parameter. Publish it to GitHub and import it into a test site. Verify the shortcode works by rendering an image with both standard and WebP fallback.
Mini Project: Build a Component Library as a Hugo Module
Create a reusable component library distributed as a Hugo module:
- Initialize a new Hugo module with
hugo mod init github.com/yourusername/ui-components - Create the following components:
- A
noticeshortcode withtype(info, warning, error) andtitleparameters - A
breadcrumbpartial that generates breadcrumb navigation using.Ancestors - A
paginationpartial with numbered page links - A
tocpartial that renders heading-based table of contents
- A
- Include SCSS for each component in
assets/scss/ui-components/ - Import the module in a test site and use each component
- Publish the module to GitHub and document the import instructions
Test the module:
hugo mod get github.com/yourusername/ui-components@latest
hugo server -D --disableFastRender
Verify each component renders correctly and styles are applied. Use hugo mod graph to confirm no circular dependencies exist.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro