Migrating from Hugo to Astro

TLDR: Decided to give this page a fresh look, and changed the template-engine on the way. Replaced Hugo by Astro, because it offered a more familiar template syntax. Would I recommend it for you? No!

Before you start reading this, this article suits you well if you

  • have been using JavaScript or TypeScript
  • have been using any React-like frameworks
  • are using Hugo right now and consider moving away

What I had before

The previous version of this page was using Hugo with a modified template I found online.

I was not happy with Hugo as static site builder. Notably for the following reasons:

  • I had to learn a new template syntax
  • The Hugo version I used was too old. The choice was either to spent the time migrating to the latest version or look for another alternative (thb, I don’t know how difficult the migration would have been)
  • I wanted to add more dynamic elements (e.g. include a dynamic content inside an article). I had to dig deeper to figure out how to add more dynamic elements with Hugo
  • I wanted to add dynamic og:images to each blog article. With Hugo it was not as simple as I thought. (this is not done yet with Astro either, but on my roadmap). With Astro, my hope is I could easily generate do it with JS.

The main pain point (for me) was the templating syntax, it was too different from what I was used to. Take for example this snippet, which gives a list of TIL entries:

{{ range sort (where .Site.Pages "Section" "til") "Params.Date" }}
{{ end }}

I wanted to shrink the list to 3 random entries. That is impossible for me, without consulting the Hugo documentation. (If you are using Hugo, would you have known it instantly?)

This is how I did it with Astro.

function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  return array;

const unsortedPosts = shuffleArray(await Astro.glob('../pages/til/entries/**/*.mdx'));
{unsortedPosts.slice(0, 3).map(post =>

Granted I had to figure out how to use the Astro library, but that is something I understood after reading their Tutorial, so it was easy to copy from another file. The same goes for the shuffle function, in case I am offline, I could easily copy it from another of my projects.

Why Astro?

I have been using Next.js for all my recent projects. Initially, I wanted to migrate this blog to it as well, but then reconsidered it. The framework is moving too fast for me. My less-than-one-year old projects are already behind a major version. I feel Vercel (the company behind Next.js) is going towards enterprise customers, so I am happy to explore another option.

To be honest, I did not do much research about which static site builder to use. Astro was something I saw few times on HackerNews and Twitter, and thought why not try it out.

There were few convince points which were encouraging for me:

  • TypeScript: As mentioned before, the templates are written in react style components with am mix of FrontMatter syntax.

  • I can use existing react components! This is a huge plus for me, since I have few projects written in React I can easily copy components from there.

  • Good enough documentation: The documentation is not perfect, but I can find what I want via Google Search

  • MDX Support: Migrating my existing Hugo content from Markdown should be simple that way. If I am not happy with the framework, I can easily switch to another solution (as the content is in Markdown).

Migrating from Hugo to Astro

Migrating from Hugo was pretty straightforward. Hugo FrontMatter-syntax is natively supported by Astro. Transferring the content was not complicated either.

Working with Astro felt more familiar to me (as said, for someone who works mostly with JavaScript). Compared to Hugo, I feel I can understand the template and I can easly guess a solution without googling. That was something I missed with Hugo.

Regrets of choosing Astro

Not everything was perfect with Astro. I have been working on this migration for a few months, alongside all my projects. The earliest Astro commit was on in September 2022. With astro 1.0.8. The latest version is 1.6.11 (as of November). I already fear to upgrade to the latest version. I hope the current version I am using covers all my need, and I can stick with it as long as possible. I simply want a static site builder with decent documentation. Is that so difficult?

Apart form that, the error messages are sometimes not really helpful. Many times you would face a message like this:

failed to load module for ssr: ../components/Header.astro
Error: failed to load module for ssr: ../components/Header.astro
  at instantiateModule (file:///path/node_modules/vite/dist/node/chunks/dep-0fc8e132.js:50468:15

Not really very helpful. It would hve been more useful to give a more detailed message then telling me the error is in Header.astro. Googling will tell you this is due to a typo in the imports.

Take away

I migrated this page from Hugo to Astro. Mainly since I am more familiar with the syntax and build environment. I don’t recommend you to migrate from Hugo to Astro. Do it only if you are in a similar situation like me. Otherwise, stick with what’s working best for you.