Why I Switched from Hugo to Astro

July 15, 2026 · 7 min read · 0 views

Terminal window showing the switch from Hugo to Astro
Table of contents

I Still Love Hugo

That hasn’t changed. One binary that eats Markdown and spits out static HTML. No runtime, no database, no node_modules folder with the gravitational pull of a small moon. It can’t get any more minimal, and I wrote a whole love letter about it after escaping WordPress.

But the thing that makes Hugo beautiful is the same thing that started to bug me. Everything is static. Radically static. The moment you want anything dynamic, Hugo forces you to think outside the box.

For a pure static site, that’s fine. But if you’re like me and want a website that’s compliant with German law, you need a second contact method next to your email address. A spare phone number or a contact form, basically. I don’t have a spare phone number lying around, so contact form it was. I got one working on the Hugo site, but it wasn’t satisfying. The whole time it felt like I was making Hugo do something it was never designed to do.

I might also just be a little delulu.

Either way, the question was on the table: is Hugo really the right choice for me?

Don’t Let Your Dreams Be Dreams

Back in the WordPress post I wrote that everything WordPress hands you for free, “I can duct-tape onto Hugo if I care enough”. Still holds up. The contact form is living proof. So is the duct tape.

Then I wanted a view counter on my posts, so I could build a top-posts section from real numbers.

The numbers already exist. My self-hosted Umami instance (took a closer look at v3.1.0 here) tracks every pageview. The problem is getting them onto pages that are built once and then never change.

My first idea: a script that runs before the Hugo build, pulls the views from the Umami API and writes them into the frontmatter of every post. Would work. Felt wrong. So instead I added a views field to my frontmatter and typed the numbers in by hand. With a handful of posts that’s fine.

It’s not fine. That’s me being a human cron job.

So I went full Shia LaBeouf. Just do it. Don’t let your dreams be dreams. I sat down to finally build that view counter properly.

And right before I started, I read about Astro and its islands.

Astro Islands Sounded Perfect. I’m Not Using Them.

Islands are Astro’s headline feature: the page stays static HTML, and you drop small interactive components into it exactly where you need them. A view-counter island on every post, pulling fresh numbers from Umami, while everything around it stays static. That’s my use case, word for word. On paper.

The more I read into Astro, the more islands felt like overkill for what I wanted. An island with live view counts means shipping JavaScript and having every visitor’s browser ask my server for a number that changes maybe twice a day. Nobody needs that.

So I made the most boring decision possible: no islands. The site stays completely static, same as with Hugo. Which had a nice side effect: the static HTML coming out of Astro drops into my setup exactly where the static HTML out of Hugo used to be. Nothing downstream even noticed the switch.

Maybe I’ll revisit islands later. For now the view counter ships zero JavaScript.

The Feature That Actually Sold Me: Build-Time TypeScript

On every Astro page you can write code that runs at build time. And it’s not some templating language. It’s plain TypeScript.

Sit with that for a second. {{ range where .Pages "Type" "posts" }} versus a normal for loop in a language my editor actually understands. Go templates are cursed. I said that when I switched to Hugo and I stand by it today.

So the view counter is now just a small TypeScript module. It logs into the Umami API, grabs the pageviews per post and hands them over. My Astro pages import it, call it at build time, and the numbers get baked straight into the HTML. The site rebuilds once a day and pulls fresh numbers on every build, so the counts are never more than a day old. For a top-posts section on a blog, that might as well be real time.

Simple and effective. No island, no client-side JavaScript, no pre-build frontmatter surgery.

What Else Changed

The engine swap turned into a full rebuild. Of course it did.

Tailwind instead of PicoCSS. I did a Tailwind crash course and honestly tried to do the design myself. But I would never dare to call myself a frontend developer. Shame on me: I got a lot of help from Claude with the design. And yes, I hear it, I wrote a whole rant about AI use. This is exactly the AI-as-a-tool use I meant there. Mostly.

A complete restyle. The blog looks premium now. PicoCSS served me well, but there’s only so far “classless CSS framework” takes you.

Images go through Astro’s asset pipeline. Optimized formats, responsive sizes, all generated at build time. I do nothing.

TOML to YAML. Hugo frontmatter was TOML, Astro wants YAML. Converting every post gave me more of a headache than I’d like to admit.

A floating back-to-top button and a dark mode switch. The little things.

A table of contents. Fancy floating one next to the post on big screens, and on mobile it’s simply folded out of the way.

Search survived. Pagefind indexes the built HTML and doesn’t care which tool generated it. Same idea as on Hugo, with one upgrade: back then the Pagefind binary was literally sitting in my git repo. Now npm pulls it in like any other dependency. Much more convenient.

And the Lighthouse score? Still 100 across the board. Same as Hugo. Static HTML doesn’t get slower just because a different tool wrote it.

So, Was Hugo the Wrong Choice?

No. Hugo got me off WordPress, taught me how much I don’t need a database, and ran this blog without a single incident. If your site is truly static, use Hugo. It’s great.

But I kept catching myself duct-taping around the static-ness. A contact form here, a hand-typed views field there, a pre-build script idea that still haunts me. Astro keeps everything I loved about Hugo: static files, boring hosting, no moving parts in production. And it replaces the part I was fighting with. Instead of Go templates and workarounds I now write plain TypeScript at build time.

Is Hugo really the right choice for me? It was. It isn’t anymore. And that’s fine.

Avatar of Alexander Hammer

Written by

Alexander Hammer

Software developer. Arch Linux. Self-hosted everything.

About me → ZanderHammer

Share this post

  1. #1
  2. #2
  3. #3
  4. #4
  5. #5