SSG Wars: A New Look
I've migrated the entire site from Hugo to Zola, along with a complete visual overhaul using a new theme. I thought I'd document what I learned along the way.
Migrate from Hugo?
Hugo has served me well for years, especially since this website has rarely been updated. Hugo is quite fast, reliable, and has a massive ecosystem. However, comma:
- I wanted to try something new. Simple as.
- I actually started out using a retro terminal theme, so I'm kinda going back to my roots. The sidebar layout of Anatole just wasn't doing it for me anymore. Soz, babe.
- Rust, anyone? Anyone, rust? Rust is good, I've heard. Zola rust. Zola good. Try Zola. OOGH
What changed?
The migration involved more than just swapping out the static site generator. Here's what actually changed:
Site generator: Replaced Hugo with Zola. Zola is written in Rust and compiles to a single binary. It's fast, has zero JavaScript dependencies by default, and produces static HTML that Just Works (TM).
Theme: Switched from the Anatole theme (sidebar layout) to the Terminus theme. Terminus is sexy. It's also WCAG 2.2 Level AA compliant out of the box, which is hella cool. I will probably customise it in the future, hence my fork.
Syntax highlighting: Moved from Pygments (which Hugo uses) to Zola's built-in syntax highlighting. It's faster and requires fewer dependencies. [Everyone liked that]
Configuration: Consolidated multiple TOML config files into a single config.toml. Hugo tends to spread configuration across several files, which can become a bit unwieldy.
Front matter: Converted all front matter to consistent TOML format. Hugo supports YAML, TOML, and JSON, but Zola only supports TOML. TOML is Hot Girl Shit, imo.
The result: Faster builds, zero JavaScript, perfect Lighthouse scores, and WCAG 2.2 Level AA compliance. Pretty good for a page I probably will only use every other blue moon.
The issues I encountered
Of course, no migration is complete without running into a few problems. Here's what I had to deal with:
Vercel deployment issues
Initially, I tried to deploy Zola to the same Vercel project that was hosting Hugo. This didn't work, and it took me a minute to figure out why. When I pushed Zola to the same project, Vercel kept trying to build it with Hugo commands, which obviously failed.
I thought I could override this with a vercel.json configuration file. While this is technically possible, it's more trouble than it's worth. You have to explicitly disable framework detection and manually configure the build commands.
The solution: I simply created a new Vercel project for Zola instead of trying to reuse the Hugo project. It's cleaner and avoids fighting against Vercel's auto-detection. The documentation for deploying Zola to Vercel is available here for the curious ones.
Local vs production path issues
I also ran into issues with local paths versus remote production paths when porting the site.
Zola requires correct base_url configuration with a trailing slash. This matters more than you might think. If you get it wrong, links will break, assets won't load, and your site will look broken.
I used dynamic base URL configuration in scripts/vercel-build.sh to handle preview deployments. The build script sets different base URLs for production versus preview environments:
#!/bin/bash
if [ "$VERCEL_ENV" = "production" ]; then
zola build --base-url "https://maxine.dev/"
else
# For preview deployments
zola build --base-url "$VERCEL_URL/"
fi
This ensures that links work correctly whether you're viewing a preview deployment or the production site.
Summing up
So in short:
Front matter format differences. Hugo supports YAML, TOML, and JSON for front matter. Zola only supports TOML. If your Hugo site uses YAML or JSON front matter (which is quite common), you'll need to convert everything to TOML.
Template syntax differences. Hugo uses Go templates, while Zola uses Tera (which is Jinja2-like). The syntax is similar enough that you can often port templates with minimal changes, but there are enough differences that you'll need to be careful.
Configuration structure differences. Hugo tends to spread configuration across multiple files (config.toml, params.toml, menus.toml, etc.). Zola uses a single config.toml file. This can be either simpler or more confusing, depending on how you organise your configuration.
Content directory structure. Hugo and Zola have different conventions for organising content, especially for multilingual sites. Hugo uses language names in directory names, while Zola uses ISO codes. This means you'll likely need to restructure your content directories. I was planning on having a seperate Norwegian language mode, but scrapped it as I will probably implement that later when I actually start writing in Norwegian (((:
Asset processing pipeline differences. Hugo processes assets through its own pipeline and can do things like image resizing and minification. Zola serves static files as-is. If you rely on Hugo's asset processing, you'll need to handle that differently. Automate that shit.
Taxonomy system differences. Both Hugo and Zola support taxonomies (like tags and categories), but the configuration and usage patterns are different. You'll need to reconfigure your taxonomies in Zola's config.toml.
Shortcodes vs template macros. Hugo uses shortcodes for reusable content blocks, while Zola uses template macros. The concepts are similar, but the implementation is different. You'll need to port your shortcodes to Zola macros.
Conclusion
Migrating from Hugo to Zola should be, and is mostly, trivial. The site can now serve 💅✨ content that AI scraping bots will read even faster. Hurray!
If you're considering a similar migration, my advice would be to start fresh with a new Vercel project rather than trying to reuse an existing one. Pay attention to your base_url configuration, especially for preview deployments,and be prepared to spend time converting front matter and templates.
And that's it! Hit that subscribe button and ding that notification bell, or whatever the terminally online do nowadays.