I switched to Eleventy

Published:
Updated:

When you run your own website—particularly one that includes a blog—you get the privilege of writing, on that website, about building the website. You even get to write about how much of a stereotype it is for a person who built their own personal website to post about how they've built their own personal website. Having now done the latter (if perfunctorily), I shall proceed to do the former.

I decided to switch the static website generator that I use from Hugo to Eleventy. I have been using Hugo for some time—I wrote a post about switching to it, back in February 2020—and it is an entirely fine static site generator. Hugo has been around for a while, seems to be fairly popular, is stable, performant, and generally does its job. However, it seemed like certain things that I found tricky to do with Hugo, and some things I potentially might want to do in the future might be easier with Eleventy, and so I decided to switch.

What's an Eleventy

Eleventy, also spelled 11ty is a static site generator written in JavaScript. Eleventy describes itself as a "simpler static site generator". In its overall design it is, indeed, simpler than some of the fancier static site generators out there. The simplicity does not mean that its functionality is limited, though. Rather, Eleventy offers a simple base which can be extended, and upon which projects of various complexity can be built.

By itself, Eleventy supports maybe eight, nine, or ten template languages (depending on how you are counting). These include Liquid, Nunjucks, WebC (Eleventy's own thing), and EJS. In addition, Eleventy makes it possible to add custom languages. While custom languages will not be as well-integrated with Eleventy as the ones it ships with, the custom language facility is still useful. For example, while Eleventy does not support Sass natively, adding support can be accomplished by pulling in the Sass library (from NPM), and telling Eleventy to process any .sass or .scss files with it. This takes less than a dozen lines of JavaScript.

New template engines are not the only thing that can be added in Eleventy. For example, by default, Eleventy can load data from JSON files, and the fields from those files can then be used in templates. However, support for other formats can also be added, by passing in a function that takes the input file and outputs a JavaScript object, so support can be added for formats like YAML, or even KDL if feeling fanciful. Another example is the Markdown library which Eleventy uses: the library can, by itself, use plugins, and Eleventy has a way of using an instance of the library with arbitrary plugins added. Scripting can be used in a lot of places—templates themselves can be JavaScript files, for example.

The project also brags about its performance. While Hugo is faster than Eleventy, Eleventy's own benchmarks indicate that it tends to be faster than a bunch of other popular JavaScript static site generators.

Migration

I decided to port the website over to Eleventy without making any other large changes in the process. The idea was to get to a working state first, so that I can then make further changes, but also can publish new posts, without having to maintain a fork of the old version at the same time.

Because I like Nix too much, I also wanted to both have a way of building the website as a Nix derivation, and also to have a Nix shell capable of running a live preview server. With the previous site, I had direnv configured to load Hugo via a Nix flake, which meant that I could clone the repository, rundirenv allow in it, and be ready to go.

While Eleventy is not in Nixpkgs itself, it is easy to install with Yarn, and mkYarnModules can be used to produce the required node_modules without overrides. Eleventy itself can be pointed at that node_modules, by using the NODE_PATH environment variable, which works for both putting a working Eleventy in PATH, and building the website as a derivation.

The actual blog content was not particularly difficult to port. Both Hugo and Eleventy support Markdown, and that is what my articles are all written in. I also use custom shortcodes, but those were easy enough to rewrite for Eleventy. Hugo uses Go templates, while Eleventy, by default, pre-processes Markdown as Liquid templates. This means that the syntax for invoking shortcodes is slightly different, but in practice they are similar enough that a simple search-and-replace is largely sufficient.

The non-blog portions of my website involved some more free-form rewriting. Eleventy is less rigid than Hugo when it comes to the directory structure of templates and layouts. This is handy for the less frequently edited portions of a website, as they can be kept more simple. Eleventy is also fairly easy to debug—there is plenty of opportunity to console.log() the state (pretty-printed) at various points, even inside templates.

The downside of the simplicity is that some things are harder to accomplish. For example, under Hugo, a page can be a directory with an index.md, and other, arbitrary files. Hugo will copy these other files as attachments, respecting the path settings in index.md's front matter. Eleventy, on the other hand, does not really have the concept of a page and its adjacent files in the same way. Fortunately, the extensibility of Eleventy means that such functionality can be bolted on, and there, indeed, already are plugins (see Eleventy issue #1540 if interested).

Comparison

Comparing static site generators is tricky, because because the category of static site generator encompasses a wide variety of tools. Some generators make it really easy to get going with a blog, and automatically handle things categories, tags, or web feeds; other generators come with less blog-specific functionality, but are more suited for a wide variety of different kinds of websites. Some generators produce traditional, static HTML sites, while others are meant for web apps.

Prior to Hugo, I used Lektor. Lektor is less rigid than Hugo. Instead of making assumptions about how input data should be structured, it requires explicit declaration of the structure. I left Lektor for Hugo, and Hugo's advantage was the fact that it comes with all the batteries included, and it mostly did everything I needed it to out of the box, where Lektor required plugins or patching. Then, I switched to Eleventy, which handles input data in a less structured way than Lektor, while also relying on extensibility rather than inclusion of batteries.

Eleventy works well for me, for what I want to achieve, and so I considered the switch worth it. That does not make it a strictly best static site generator, and whether or not it will work for anyone else depends on their individual use case and needs. Even things like speed benchmarks are relative, since speed can matter less for smaller websites (such as my own). On the other hand, Eleventy is easy to play with, and relatively well-documented, so I can recommend looking into it, if it seems like it would fit your requirements.

Addendum

I published a repository, which serves as an example of how an Eleventy website can be built as a Nix package, while also providing a direnv environment for working on it..