Skip to main content
Ungathered Thoughts

From Jekyll & Hugo to Eleventy

Well, I seem to have made it through a Jekyll and Hugo to Eleventy conversion, and come out the other side with a set of posts going back a decade. Hail plain text formats!

Eleventy has been an accessible (for me) platform to do this. What have I had to tackle?

A variety of sources

The posts collated together here cover a few years of a "personal engineering" Hugo blog I've kept internally at Catalyst, and about as many years of Jekyll blog I'd stopped updating when I joined Catalyst. Two similar (markdown-based static site generator) systems with subtly different data sets. The frontmatter for both engines was different, and I did a little juggling with perl find and replace to rearrange things. I had both categories from Jekyll and tags from Hugo, for example.

It could be tempting to perform a "migration" from Hugo and Jekyll to Eleventy, but one lovely aspect of Eleventy here was that it was flexible and fast enough for me to fix things on the fly. So I didn't need to do that.


Eleventy uses tags differently, but in a way which works for me. Specifically, all posts are members of the "post" collection, via a src/posts/posts.json data file which sets that tag on the contents of the directory. When displaying tags for an article, that means I filter out the tag "post", such as at the top of this page.

On the front page of the site, I surfaced the tags for each post - this was mostly a lazy way for me to visually check if any posts were missing tags. (They are. I'll get to them.)


For a long time on at least one of these blogs, I was hand-entering something like ISO dates. And sometimes got them wrong, cos I forget small things. So I wrote a couple lines of filter to handle most any date that the posts would throw Eleventy's way.

eleventyConfig.addFilter("postDate", function(dateInput) {
let dateObj = DateTime.fromJSDate(dateInput);
if (dateObj.invalid) {
dateObj = DateTime.fromISO(dateInput);
if (dateObj.invalid) {
dateObj = DateTime.fromFormat(dateInput);
return dateObj.toLocaleString(DateTime.DATE_MED);

Syntax highlighting

Eleventy had PrismJS built in and that worked well once set up, but I had to nudge a few formats about - in my Hugo blog I'd used "cron" format which PrismJS didn't like, and so on. So, a little content mop-up required.

This isn't quite working right yet - I'm seeing things highlighted but combined into a single line on deployment. TBD.

Tailwind CSS

I tried Tailwind CSS, and I've decided it's perfectly good. I wasn't sure at first. It's a whole vocabulary I'd need to either pick up or look up to operate with, which is something. But it's produced a tidy, painless layout which I'd otherwise have probably spent ages getting approximately right.

I'm using eleventy-plugin-tailwindcss which seems to work great.


I had posts with draft: true set, and didn't want to drop them all on the world. I used eleventy-plugin-ignore.


My previous Jekyll blog had dates in the filename (posts/2023-03-06-jekyll-to-eleventy) but not in the paths (/jekyll-to-eleventy). I didn't want to have to handle the redirects, so I used Eleventy's per-directory JSON to rewrite paths.

In .eleventy.js:

eleventyConfig.addFilter('stripDatePrefix', str => {
return str.replace(/^[0-9-]+/, '')

In src/posts/posts.json:

"permalink": "/{{ page.fileSlug | stripDatePrefix }}/"


I'd previously used a {% gist <url> %} and needed to convert this from Jekyll.

eleventyConfig.addShortcode("gist", function(url) {
return `<script src="${url}.js"></script>`;

Large media and hosting

I'm already deploying this to Gitlab Pages, easy. However ... I want to have a slightly more flexible and funcitional hosting environment. So I'll wire up a deploy over SSH/rsync from Gitlab CI.