
A rendering of Eleventy's mascot by Phineas X. Jones.
As the title says, this blog is now generated with Eleventy, or 11ty as they call it. I thought I would write a little bit about my experience translating this blog, which comes from a static Wordpress, from Jekyll to 11ty.
I have opinions, so skip what doesn't interest you:
In the beginning was Wordpress §
I keep saying that static web sites are the best. The modern web feels bloated and unnecessarily wasteful to me, maybe I'm getting old. Static web sites are a better web: content-centric, fast, easy to deploy and cheap to maintain.
I started this attempt to a personal blog many years ago with Wordpress, with the 8-bit theme from Brutalist Themes (which came with a GPL license!). Then I adapted the WP2Static plugin so it would generate a static website, using Wordpress as a kind of WYSIWYG web editor. It was a fun project but very difficult to maintain, and at some point I lost the instance that stored the Wordpress setup.
Jekyll is great, but... §
Some years ago I reconstructed the web site from Internet Archive snapshots, but this time using Jekyll. Why did I choose Jekyll? Well, it was the Wordpress of static web sites. I imagine GitHub choosing it for their Pages made it explode in popularity. I must say that for simple things it "just works": you put your posts in ./_posts
, add some HTML templates with Liquid syntax, and that's it.
It served me well, and as a programmer it gave me the flexibility to implement my own plugins to do fun stuff: decorate external links, tables of content, etc. Was it necessary to reimplement that? No. Was it because there weren't plugins available? Again, no. But was it fun? Yes! And that is the whole point of having my own blog, doing unnecessary and funny things.
Obviously there is a "but" at the end of the previous paragraph, because Jekyll had its shortcomings.
For starters, the decision to implement Jekyll in Ruby. I liked learning a little bit of Ruby, but the tooling felt clunky, at least for a simple blog. I never got used to gems and rvm
, it felt very difficult to set up. Another pain point was the performance, which isn't a big problem for a small blog, but it seemed unnecessarily low nevertheless. In addition, Jekyll rapidly feels rigid and verbose if you have to slightly deviate from the expected functionality. It may not be alone in this, more below.
Another reason is the status of the project. Every time I visited the GitHub page of a plugin, the last commit was 5 years old or more. This is subjective, and Jekyll could instead be thriving, but it gave me the impression like it was slowly dying.
It is at this point when I heard about Eleventy...
Hi, my friends call me 11ty §
I started following 11ty's Mastodon account and seemed an interesting project. And, as an engineer, I cannot resist the temptation of a big, unnecessary refactor and "just rewrite it in Rust 11ty".
Jekyll users will feel using 11ty very natural: the _posts
or _includes
directories, rendering the static files to a _site
directory, support for Markdown and Liquid, and so on.
I also count 11ty's tooling as an improvement. And by that I mean NodeJS tooling. npm
and package.json
felt like an improvement. And, in general, the Javascript community is huge, so 11ty has access to so many more packages.
Before going on to more details: both Jekyll and 11ty have great documentation sites, well written and with many examples. My only complain about 11ty's documentation site is that the division in chapters only made sense after I understood the basic concepts of 11ty. Also, the search function is not great (try searching for setLayoutsDirectory
), but I guess that's what happens with static sites. It was easy to get lost, to the point that I wrote a Python script to scrape the documentation web site and store it as a PDF, which then I fed to a NotebookLM project.
There is no official guide to migrate from Jekyll to 11ty, so this is the general plan:
- Translate your
_config.yml
toeleventy.config.js
. - You can keep your posts in
_posts
and your layouts in_includes
. By default 11ty doesn't distinguish between layouts and includes, if you use the_layouts
directory you'll need to turn onsetLayoutsDirectory()
in the configuration. - While using
{% include %}
to compose templates is a common practice in Jekyll, in 11ty it depends on which render engine you are using, and in general you lose thepage
context. I had to slightly modify my template structure and use LiquidJS{% renderFile %}
with the explicit state that it required. - 11ty supports many template engines, beyond Markdown and Liquid. In particular 11ty's native Javascript templates and global data files are a powerful tool. Beware: 11ty's Liquid flavor is slightly different than Jekyll's, so it will need some massaging. For instance, 11ty doesn't support
markdownify
. - The overall data flow used by 11ty is similar to Jekyll: posts with a front matter get rendered by templates, which can be composed with layouts and includes. However, while Jekyll has
pages
andposts
, in 11ty everything is justdata
which can be grouped within acollection
. I think this is a natural evolution, but the posts/pages organization will be more familiar to people coming from Wordpress. - Adding custom filters or shortcodes is as easy, if not more, as in Jekyll.
If you only have a bunch of loose Markdown files in a _posts
directory, with some templates to render them, that's more or less it. But anything more complex starts getting more challenging.
What the hell is a pagination? Understading 11ty's data flow §
Personally, it took me a long time to understand the data flow process within 11ty, and I still struggle. This is my mental model:
The data items can be sourced from files ❶ (for instance the _posts
directory, or the index.html
page), from data files ❷ (like Jekyll, 11ty will fetch global data files from _data
or from local data files) or generated programmatically ❸ (for instance using addGlobalData()
).
These items/posts/pages can be added to collections using the collections API ❹❺. Collections are accessible in the templates with the handy collections
object.
Then, there's pagination
❻❼. I really struggled with this, because it conflates several functionalities in a single mechanism, but it boils down to:
The Pagination feature is used for iterating over any data to create multiple output files.
A better way to describe it, in my opinion, would be: Pagination renders m data items onto n HTML pages.
For instance:
- m data items in 1 page: an archive or home page.
- m data items in m / k pages: an archive k items per page.
- m data items in m pages: render a collection of posts, each one in an independent page.
But there's something more to the pagination
mechanism. The data items coming from pages and templates ❶ are page
objects ready to be rendered, while data items ❷❸ need to be transformed ❼. This is not just a methaphor. Even if they share some pipeline stages, like the collections or pagination APIs, they are different kinds of objects and somehow 11ty knows this.
For example, the data items coming from posts or templates ❶ will come with a front matter and several other properties, which are accessible in the early stages, like the collections API ❹. This means that front matter properties of a post can be accessed and modified, and that will be propagated to the page.data
object when rendering it ❾.
That is not the case with a raw data item ❷❸. Modifications to the data item ❺ will not be propagated automatically when it is processed by a pagination ❼. Technically, pagination
allows to specify the front matter properties of the page that will be rendered from a data item. However, only permalink
can access the data item. Yes: no other front matter properties can access the data item.
Then, how to make properties of a data item available to the templates? In other words, how to go from this:
{
"title": "A Tale of Two Cities",
"lang": "English",
"excerpt": "It was the best of times, it was the worst of times."
}
To this:
---
title: A Tale of Two Cities
permalink: /en/a-tale-of-two-cities/
---
It was the best of times, it was the worst of times.
As the documentation says:
Note that only the
permalink
andeleventyComputed
front matter values can contain variables and shortcodes like you would use in the body of your templates. If you need to use variables or shortcodes in other front matter values, useeleventyComputed
to set them.
This is where computed data
❽ comes into play, which is a stage between pagination and rendering, and allows to specify front matter properties calculated from the original data item.
Extensibility, extensibility everywhere §
When the basic template structure of the web site was done, I needed to port my custom plugins. 11ty comes with some fancy plugins that are drop-in replacements, but what I liked the most is the use of Markdown-it, which has many nice plugins itself and makes it easy to add your own. 11ty helps with this through amendLibrary()
.
This is how I could add tables of content or footnotes. For other stuff, like my own plugin that optimizes image sizes or the static full text search, it was quite fast to implement in Javascript.
If implementing my own plugin provided a little extra and wasn't incredibly difficult, I avoided the official ones. This allowed me to experiment with most of 11ty's features. I think the only functionality I didn't touch were the preprocessors and transforms. Maybe I will revisit my custom plugins, as I still have to check the many official plugins.
Would buy again §
So, overall, the whole experience has been positive, and I feel like Javascript is a much better language to do this kind of rapid development, especially when it is also used in the rendered page. On the cons side, I still feel like Jekyll and 11ty are designed to be very easy to use for simple use cases at the cost of being difficult to extend sometimes. While 11ty makes it easier to generate pages programmatically using pagination, accessing the existing data can be almost impossible in some parts of the flow.
Sometimes, the data flow can be difficult to understand. Why only permalink
can be computed from data items, while other fields require computed data
? Why I global data and collections can be accessed in some places, while not in others? This issues are not unique to 11ty, and Jekyll also suffers from it. Most likely there are good reasons for these design decisions, but to a foreign observer (with too much time and refusing to use the official plugins) it can be puzzling.
Anyway, it has been a fun project and I think 11ty makes developing your own static blog a nice experience.