The Two Ways to Extend Hugo Sites

When you want to add themes or plugins to your Hugo site, you have two main options: git submodules or Hugo modules. Of course, you could just download and copy files manually, but we’re trying to be rational here.

Hugo modules are the newer approach, officially recommended by Hugo developers, ChatGPT, and Perplexity. However, if you’re comfortable with Git, submodules work perfectly fine and that’s what I use.

Why I chose git submodules

Transparency and customization

With git submodules, you can directly view and modify theme files. Want to tweak a template or understand how something works? Just navigate to the theme directory and explore. It also makes life easier for Copilot/Cursor since it can access the actual files without any extra steps.

With Hugo modules, you need an extra step: run hugo mod vendor first to populate the _vendor directory, then explore the files there. Don’t forget to clean it up afterward when updating modules.

Dependabot integration

Dependabot is fantastic: it tracks dependencies and creates automatic update pull requests with release notes and changelogs. Git submodules are fully supported by Dependabot.

Here’s my Dependabot configuration for automatic updates:

# .github/dependabot.yml
version: 2

updates:
  # Update submodules automatically (e.g. themes)
  - package-ecosystem: gitsubmodule
    directory: /
    schedule:
        interval: weekly

Warning

Unfortunately, Dependabot doesn’t support Hugo modules yet. The open issue from 2023 remains stale because Dependabot uses standard Go commands for modules (go get and go mod tidy) instead of Hugo-specific ones (hugo mod get and hugo mod tidy), which delete your go.mod and go.sum files. Of course, you can create a custom action, run commands manually and use Create Pull Request GitHub action, or simply switch to an alternative solution like Renovate, but again, this requires extra effort.

Info

If you want to update submodules manually, I use: git submodule update --remote --merge. This command comes from the second Stack Overflow answer and is better than the first accepted answer because it works regardless of whether the remote branch is called main or master.

Adding a theme as submodule

If I managed to convince you, here’s what you need to do:

# Add theme as submodule
git submodule add https://github.com/author/theme-name.git themes/theme-name

# Initialize and update
git submodule update --init --recursive

Don’t forget to add the theme in your config file (config.toml, config.yaml, or config.json). For TOML: theme = "theme-name" or theme = ["addon-name", "theme-name"] if you already have multiple themes/addons.