Previously, I use Jekyll to build my website. After careful consideration, now I decide to switch to Hugo. The main reason is straightforward:

Speed.

Jekyll does not scale

It all looks nice and great when you look at Jekyll’s official website. My initial reasons for choosing Jekyll include its integration with GitHub Pages, good documentation, wide range of themes and plugins. But when I have some 20 posts, it takes me 5~6+ seconds to build the site, and it takes around 4 seconds for reflecting edits in live preview, which is almost unbearable! Jekyll is too slow. I cannot imagine what would it be if I had 100+ posts.

Another reason for me to ditch Jekyll is the difficulty in customizing the theme I was using. As I publish more and more blog posts, gradually I would like to improve my website’s appearance. For example, I felt the body’s font size was too small and I wanted to increase the font size. But the theme I was using has hundreds of lines of SCSS code spread across multiple files, and has many JS files that are unclear to me how they affect the website. Sometimes I spent hours trying to figure out where a font size or color is defined, and how to change them appropriately without affecting other parts of the website.

Writing blog posts in Jekyll is simply not sustainable. As my blog posts grow, Jekyll becomes unmanageable. I guess it is probably only best suited for portfolio sites and landing pages?

Hugo is fast

On the other hand, Hugo is fast. Build is instant. Many frameworks’ build times grow linearly with the number of documents, but Hugo’s is a trivial constant. Go is indeed fast, as we all know.

To use hugo, first download golang and hugo. cd to your website directory, use

hugo serve

to build your site in dev mode. Saved changes will be instantly reflected on your screen. You can also define a post template in the /archetypes folder, then when you do

hugo new posts/title.md

the command will create a new content file in /content/posts using the template. The command

hugo

will build your website and output static files in the /public folder. You can now push this folder to GitHub or somewhere similar to trigger a CI/CD workflow.

Of course, there are some trade-offs:

  1. Hugo’s documentation has poorer quality, and its template language has a steeper learning curve than Liquid.

  2. I still need to rely on a theme. I’m using the very nice PaperMod theme, created by Aditya Telange, and that means my website will look similar to other people’s websites.

The default font for code blocks is not very smooth. I added the following line

font-family: "Menlo", "Monaco", "Consolas", "Courier", monospace;

to .post-content code and .post-content pre code in themes/PaperMod/assets/css/common/post-single.css.

  1. Not convenient to use TailwindCSS.

  2. There is no out-of-the-box solution to include citation and bibliography in posts. While Jekyll has a nice plugin called jekyll-scholar, currently there is no equivalent in Hugo.

2023-08 update

Currently, I’m using the hugo-cite theme for managing bibliographies. I downloaded the theme into the themes folder, specified it in config.yml, and added the line

<link rel="stylesheet" type="text/css" href="{{ "/hugo-cite.css" | relURL }}" />

to the PaperMod theme in themes/PaperMod/layouts/partials/extend_head.html. The default template prints each citation key in front of each entry in bibliography list, which I don’t want, so I deleted the code enlosed in <dt> tag in themes/hugo-cite/layouts/partials/bibliography-list.html, and also deleted the <dl> tag for correcting line break issues.

I use pandoc for converting .bib file into CSL-JSON format. The command is

pandoc ref.bib -t csljson -o bib.json

I place the two files inside the /content/data folder.

JavaScript frameworks are not perfect

As a fan of Next.js, of course I considered the option of building a blog website with Next.js. I like its client side navigation. And with JavaScript I could easily add some interactivity to my website, which could deliver a better user experience. The biggest problem with Next.js is that, it has no built-in support for markdown! You need to install packages like @next/mdx, mess around with its configurations, and use it like so (taken from the docs):

import HelloWorld from './hello.mdx';

export default function Page() {
  return <HelloWorld />;
}

That’s too much of a hassle, and it’s unlikely that you will encounter no bug when dealing with these extra steps.

There is a nice-looking Next.js + Tailwind template, but it uses Next.js 12, not the latest Next.js 13 (app directory), and it has so many components that it is very difficult to customize.

In general, Next.js lacks support for math (latex) and programming oriented blogging, so it is not the best option. Build time could also be a problem. Next.js is not very fast. Even though it has a new Turbopack bundler written in Rust, it is in alpha and that means it’s too buggy to the point that it’s almost unusable for now.

I also considered another well-known JS framework, Astro. The nice part about Astro is that, with JavaScript, you can build your own site from scratch, or adapt others’ templates easily. Styles can be defined in Astro components along with HTML and JS within one file so there is less headache for CSS styling. Being able to use TailwindCSS is also a big plus. But speed is still an issue. Astro claims to be fast, but Node.js is not really comparable to Go in terms of speed. I tested with 200 posts. Although live preview is fast, it takes really long time to build, so it doesn’t scale well.

Svelte looks very promising, but it is too early, and there aren’t enough templates. 11ty looks interesting, but its documentation is not very clear. I wouldn’t use Gatsby since it has its own problems. There are many other JS frameworks out there, but none of them is perfectly suitable for the current scenario.

Conclusion

Building a blog website is, surprisingly, not an easy thing! Although you have many choices, each has their drawbacks that may not be apparent until much later after your usage. Using a framework always lead you to some degree of framework lock-in. It is difficult to migrate to other frameworks when you find that the framework is no longer working for you anymore. For me, speed is now the biggest factor, and Hugo has no competitor in this aspect.