From Hugo to Next.js and Back Again

This post features my challenges, frustrations, and why I ultimately landed on using Hugo as a static site generator to build my blog. The past three iterations of this website were written using Hugo, Next JS, and then back to Hugo again. Hugo gave me so much out of the box, and practically everything I needed, whereas Next JS, required me to write the functionality I wanted explicitly. This article is not meant to hate on any frameworks and instead voice my own experience.

Choosing a Static Site Generator

Since I’m primarily front-end focused and quite confident building almost anything with React, I figured it would be a great idea to rebuild my blog using a React-based static site generator. I began my search by researching React static site generators and managed to narrow it down to three possibilities: React Static, Gatsby, and Next.js.

React Static: Looked interesting and was built to solve some of the problems that Gatsby and Next.js had. Here is an article about why Nozzle built React static in the first place. After digging in and playing around with a sample project, I felt as though the documentation and examples were not enough for me to feel productive. Therefore, I decided to pass on this one.

Gatsby: Another exciting project, and quite a popular option. Gatsby gives you so much out of the box, and there is almost a plugin for any functionality you need. I loved that Gatsby is built in a way that puts performance first by rendering what the user needs right away and aggressively prefetching the rest.

What ended up turning me away from Gatsby was the stability of the framework. I had numerous occasions where I had to restart the development server because of an unknown error, or the livereload would suddenly stop working. I also found that there was not enough information while trying to debug why a particular GraphQL query wasn’t working as expected or remark properties were randomly missing from the schema.

Next.js: I had heard lots of great things about this framework and a few coworkers had used it to build marketing type pages. Vercel (the company behind Next) is also very active in the open source community and has several high profile projects on the go. After checking out the documentation, it had interactive tutorials which I thought was cool and a bonus compared to other frameworks. Next was built initially for building isomorphic web applications and recently added the option to export static files.

I ran through the get started tutorial and found it was incredibly simple to get started. By adding Next as a dependency, creating a pages folder with a single index file, and running the application, it just worked. I slowly kept adding and exploring the framework and enjoyed how it was coming together. This experimental phase gave me confidence that is framework would be an excellent choice.

Building the Blog with Next.js

After going through the basic tutorials on the Next site, I was able to quickly build something that worked. One area where I got stuck was creating dynamic pages which worked with the static export. I learned that if you want dynamic pages, you either have to include it as part of the configuration or include them in the pages’ directory. This meant whenever I wanted to create category pages, I had to parse the source files to collect all categories, and then generate pages based on that. Not quite as easy and straightforward as Hugo, but it wasn’t that bad.

The stack I used to build this blog on top of Next was MDX, TypeScript, Emotion, Redux, Typesafe Actions, and Redux observables. Most of the inspiration on how to lay out the directory structure and generate posts using MDX was directly from Next’s site.

  1. I had fun building the blog using Next.js, but there were a few points that made me wish I had gone a different route.
  2. I had to write a few custom plugins to add simple functionality like the reading time or table of contents to a blog post. This required me to parse the MDX files by taking the AST and transforming it or appending to it.
  3. The JavaScript bundle size (170.1 KB) was reasonably large for a simple blog.
  4. Generating a list of posts required a bit of extra work compared to other frameworks like Gatsby. Gatsby makes all posts query-able from any page, while Next requires you to generate that list yourself by reading the file system. To achieve this, I had to generate a summary based on the contents of the blog post. I wrote a script which copied all the exported frontmatter and saved them into summaries which I was able to import and render.
  5. There were not as many plugins available as Gatsby has which would have saved me some time.
  6. I probably spent way too much time trying to configure TypeScript to work with Emotion. By the end of the project, I still had parts of the application which threw TypeScript errors.
  7. Redux and Redux Observables are overkill for what I needed them to do. I was using it for managing the like button state, which could have easily been replaced by the React context API. In my defense, I had plans of expanding the functionality and making the website more interactive and state driven.
  8. I learned that using Emotion was not as joyful as I thought it would be. I love the component approach to every single element, but in practice, I feel like this made the code partially unreadable. It added confusion about the element and hierarchy instead of simplifying it. I was frequently examining each element to see if it was a div, span, or a header.

By the time I had completed an MVP and deployed it to production, I was left not quite as happy as I thought I would be. Yes, the user experience and performance were great, but I felt like a lot of the little things I wanted, had to be hacked together.

A couple of months went by, and I decided to redesign my blog once again. This time I kept in mind of my experiences and decided it would be best to build it again using Hugo.

Rebuilding the Blog with Hugo Again

Now the question you might be wondering it is why I would switch back to Hugo? Simply put, Hugo has all the functionality I need at the present moment out of the box. These days my time is limited, and I want to work on other things than my blog, so I opted to keep it simple and live within Hugo’s constraints.

I began by using the Victor framework by Netlify to give me an excellent foundation to build upon. It has everything you need to get started with Hugo, modern JavaScript, and any CSS configuration you need.

This time around, I decided to use Tailwind CSS, BEM, and the ITCSS methodology to style my website. I had used Tailwind on the first iteration of this blog and loved using it. For those not familiar with Tailwind, it’s a utility-first CSS framework that essentially provides a series of classes to reuse to build out components.

To keep that single page app feel, I used Turbolinks and Stimulus to load and handle page events. This was my first time using Stimulus, and I found it pretty great when you need to sprinkle some functionality within a server-rendered app. Stimulus paired with Turbolinks works amazing together since you no longer need to watch for page changes and refresh the JavaScript. In the past, you needed to watch for the turbolinks:load event and then update all event listeners, elements, and anything your JavaScript code was doing each time the page changes. Stimulus removing that overhead was a nice change.

To make things a tad faster, I run the CSS through Purgecss (which is pretty much required when using Tailwind), then inline critical CSS, minify HTML, and defer loading CSS and JavaScript files. By applying these performance enhancements, the website feels fast on all browsers and devices I tested, and I was able to achieve 100s in all categories when using Google lighthouse. Best of all, the CSS and JavaScript files are minuscule compared to what I had built with Next JS. At the time of writing this article, the main CSS file is a whopping 6.6 KB gzipped, and 34.4 KB gzipped for the JavaScript file.

Lessons Learned

One of the core recitations I learned was to pick the right tool for the job. As much as I love using React, it just didn’t suit my needs for this one website. Many of these frameworks may seem tempting, but ask yourself if is it essential for what you’re trying to build. A great example of this is how Netflix replaced their sign-up page with vanilla JavaScript to speed up the loading and time-to-interactive. I do wish more companies would take note of this as you may not need a full-fledged framework/library for something relatively simple.