Go Hugo? Go Astro!

29 May 2025

Every now and then I like to try out new technologies. Astro has been on my list of things to try out for quite some time now. When I was playing around with content collections, I used the content on this website as dummy data. Soon after that, I noticed that the playground Astro project not only looked better than my original website built with Hugo, but also offered a better developer experience with more readable code.

This post is my attempt at putting into words why I feel that Astro is running circles around Hugo when it comes to developer experience.

Surface Level Comparison

Right from the start, there are a lot of obviously nice features that Astro has. It has JSX-like templating, which makes it very approachable for developers coming from the world of web development. Since it’s a JavaScript framework, it’s easy to tap into a vast world of JavaScript libraries. It even offers official integrations with front-end frameworks like React or Vue.

Astro also offers great editor support, which is further enhanced by TypeScript’s type safety.

But none of these features are what makes it stand out compared to something like Hugo.

Killer Feature

Astro’s killer feature is, to some degree, hidden as an implementation detail, and I don’t even think it’s mentioned explicitly anywhere in the docs. That is composability.

Astro has the same level of composability you might be used to from React, Vue, Svelte, or similar frameworks. Everything is a component that can be called from anywhere and can take any other component as a child. Even layouts are just components:

Layouts are Astro components used to provide…

This component oriented design, combined with the type safety provided by TypeScript, introduces very profound differences between how one creates an Astro website and how one creates a Hugo website.

To explain the differences, let’s imagine you are thrown into an existing project. Now you want to change the rendering template a bit. How do you find the template based on the URL of the page? In Astro, routing is simply file-based from the pages directory. You open up the <page>.astro file, and the whole “HTML structure” is very explicit. Because of composability, the layout will be specified explicitly as a component with some children.

<Layout>
  {/* Some children */}
</Layout>

Want to jump to the layout’s definition? Just press “go to definition” in your editor and it will take you there. Want to check which other pages rely on the layout? Simply press “find references” in your editor, and it will list all the occurrences. Want to check the parameters that the layout takes? Just look at the Props interface. And the best part? This is not specific to layouts - you can do this with any component you want.

Now compare that to how Hugo does it. Because Go HTML templates lack proper composability, template lookup is implicit. There is enough reading material for the whole evening just to understand the lookup order! And there are even more rules for the different page types, partials, home pages, list pages, … And remember, no editor will help you go to definition here. You want to know what the available values and methods in the render context are? Well, you’re out of luck. You just have to sift through the docs or ask an LLM and hope for the best.

To put it in shorter terms: because of the composability, in Astro everything is explicit, whereas in Hugo everything is a pile of implicit rules. Combine that with the editor support Astro has - or more specifically, the lack of editor support for Hugo - and it becomes clear that there isn’t even a competition between the two1.

Nothing is Perfect

Nothing is perfect, and neither is Astro. The biggest weakness is that it’s built in JavaScript. This makes it very slow compared to Hugo. If Hugo has hot reload, Astro has more of a lukewarm reload. And the difference is even bigger when it comes to build times.

This is probably just me, but I’m also not very ecstatic about using npm. I can just feel the mountain of dependencies that Astro is pulling. After running npm outdated on a three day old project, it already reported outdated dependencies. After just three days!

Conclusion

Before this experiment, I had only ever built websites with pure HTML, Next.js, and Hugo. After using those, Astro feels like a breath of fresh air. Nothing comes even close to its developer experience and features oriented toward content heavy websites - and that’s after just scratching the surface. And because the end result is static HTML, I can live with all the JS dependencies, lukewarm reloads, and slow build times.

So just like that, Astro became my favorite way to build static websites and also the tool I’m using on the website you’re currently reading.

Footnotes

  1. Don’t get me wrong, Hugo is still a great project, and (in my opinion) the true limitation lies in Go HTML templates. Hugo really did the best it could with them.