Migrating the Beautifulhugo Theme from a Git Submodule to Hugo Modules

Learn how to transition your Beautifulhugo theme from submodules to Hugo Modules.

* This page contains promotional content

Up to now I had been using Hugo’s Beautifulhugo theme on this site as a submodule, but I switched it over to the hugo modules format.

Environment

I work by cloning my own repository from Github.

First, a look at the local environment

❯ go version
go version go1.22.6 darwin/arm64
❯ hugo version
hugo v0.131.0+extended darwin/arm64 BuildDate=2024-08-02T09:03:48Z VendorInfo=brew

The theme is beautifulhugo, installed as a submodule

Initialising hugo modules

$ hugo mod init github.com/user/scribble
go: creating new go.mod: module github.com/user/scribble
go: to add module requirements and sums:

Fetching the beautifulhugo theme

$ hugo mod get github.com/halogenica/beautifulhugo
go: added github.com/halogenica/beautifulhugo v0.0.0-20240711164100-bd5ebaab69c6

hugo.toml

Add the following, and comment out the theme entry

[[module.imports]]
  path = "github.com/halogenica/beautifulhugo"

Removing the git submodule configuration

Delete the submodule-related settings and files.

.git/config holds submodule-related settings, so remove those entries as well.

$ rm -rf themes
$ rm .gitmodules

Checking that it starts

$ hugo server -D

Once you have confirmed it starts, push to Github and you are done

Notes

Incidentally, it does not look like GO_VERSION needs to be set in the Cloudflare environment variables.

As for updating the theme from now on, I expect it will be a matter of using the hugo mod get -u command to update the theme and then hugo mod tidy to remove and tidy up unneeded dependencies.

Watch out for the _vendor directory

Running hugo mod vendor copies the contents of the theme into the _vendor directory, but if you put that in .gitignore and leave it untracked, it becomes a source of build differences between your local machine and Cloudflare Pages.

Because Hugo prefers _vendor when it exists, even if you update the theme version in go.mod, a stale local _vendor means the build keeps using the old theme. Cloudflare Pages, on the other hand, clones the repository fresh every time, so _vendor does not exist there and the build uses the latest theme as specified in go.mod.

The result can be the phenomenon of “it displays fine locally, but breaks when deployed to Cloudflare”. Clearing the browser cache or the Cloudflare cache does not fix it, because it was never a caching problem in the first place: different versions of the theme are being built locally and on Cloudflare.

_vendor is an optional feature for keeping a frozen local copy of the theme code, and it is not generated unless you run hugo mod vendor. If you do not create it, Hugo always fetches the version pointed to by go.mod/go.sum through the module proxy and builds with that, so the theme version referenced locally and on Cloudflare is guaranteed to match. Cloudflare Pages fetches over the network anyway in its hugo: downloading modules step, so the offline-build benefit of _vendor does not really pay off in this configuration.

For that reason, simply never creating _vendor (not using hugo mod vendor) is the simplest measure to stop this kind of discrepancy from happening.

See also