Iā€™ve spent the last few weeks distracting myself from things by dabbling with Eleventy (11ty) ā€“ ā€œa simpler static site generatorā€. Iā€™d been meaning to take a look at it for a while, having decided that it was time to break away from WordPress (for this site at least), and I was sure that static was the way to go. Iā€™ve used Hugo and Gatsby before, but all the cool kids are talking about 11ty, so I thought Iā€™d make the most of the lockdown and give it a go whenever I had an hour or so free.

11ty logo

What youā€™re looking at now are the fruits of that labour. If you were familiar with the old site, you probably wont notice much different; itā€™s the same basic design and structure, with only a few visual tweaks. Behind the scenes though, big tings.

All things considered, the whole process was pretty smooth, and I could definitely have been done much sooner if Iā€™d given it more than my casual attention. Iā€™ve enjoyed the excuse to learn something new and Iā€™ve appreciated the comprehensive documentation which has helped me make sense of things. Iā€™m also indebted to the people whoā€™ve made their own 11ty work public on GitHub. I didnā€™t use any of the base builds that are floating around, but I did pick up all sorts of bits from all over the place (particularly Kristoff Michiels, Sarah Fossheim, Tatiana Mac, and James Doc). My own contribution is here: https://github.com/mattnortham/ma11northam though Iā€™m certainly not presenting it as any sort of standard.

Likewise, here are a few notes on the process that are by no means a ā€˜guideā€™, but might help you if youā€™re getting to grips with 11ty (and will serve as a reminder to me when I next come to do this). Theyā€™re conveniently split into 11 points which I PROMISE was not intentional, but Iā€™ll happily accept.

  1. Getting content out of WordPress
  2. Handling images
  3. Markdown It
  4. Dates
  5. Console.logging
  6. Building collections
  7. Content management
  8. Deploying to your own hosting
  9. Performance
  10. Liquid
  11. Some other little things

Getting content out of WordPress šŸšš

I used WP Gatsby Markdown Exporter to grab my existing content. It does a great job at converting posts into the correct format and packaging it up in manageable directories (as well as a copy of all uploaded media). That bit was quick. What took much longer though, was the tidying up. I had a lot of front matter that I didnā€™t want any more, and some of the content itself needed amending (image markup & media embeds were the main culprits). I still havenā€™t finished going through all those old posts yet (Iā€™m back to 2018 so far) but though itā€™s tedious, itā€™s actually pretty useful in terms of prompting me to review and audit it all. Iā€™ve sanitised tags, fixed broken links, and formatted posts with a bit more consistency. Well worth it.

A side by side view of the exported front-matter and my amends

Handling images šŸ–¼

It took me a while to get images behaving in a satsifactory way and I still havenā€™t really got it sorted. My main problem was in serving them up from the markdown files, and though itā€™s not entirely resolved (adding a class, anyone? <figure>?), I do at least have them lazyloading (with a <noscript> fallback) after rolling my own Markdown It plugin (more on that later).

Some of the many images in my photo galleries

Iā€™ve also made use of a couple of shortcodes to render appropriately sized images in my liquid templates, which was uber important due to the number of images Iā€™ve got in the photography section. It doesnā€™t go as far as responsive images yet, but does at least show small images where I want small images, and big ones where I want big ones. A little bit like this:

// Usage: {% smallImg "PATH-TO-IMAGE", "The alt", "align-left" %}
smallImg: (img,  alt,  className) =>  {
let i = img.lastIndexOf('.');
let imgPath = img.substring(0, i);
let ext = img.substring(i +  1);
let size = '-sm';
let theImage = `${imgPath}${size}.${ext}`;
if (className == "lazy-img")
return `<img data-src="${theImage}" alt="${alt}" class="lazy-img img-small ${className}">
<noscript><img src="${theImage}" alt="${alt}" class="img-small"></noscript>`
else
return `<img src="${theImage}" alt="${alt}" class="img-small ${className}">`
}

Markdown It šŸ—’

Speaking of Markdown It, setting the typographer option is a must " " šŸ‘€.

const mdOptions  =  {
	html:  true,
	breaks:  true,
	linkify:  true,
	typographer:  true
};

The plugin I bodged together to wrap images (which you can see here) was inspired by markdown-it-plugin-data-src.

const imgDataSrc  =  require("./src/_11ty/[imgDataSrc.js");
...
eleventyConfig.setLibrary("md",  markdownIt(mdOptions).use(imgDataSrc));

The reason I made my own was because I wanted to use a different lazyloading method, as well as the <noscript> too. For the JS, Iā€™m making use of vanilla-lazyload, as thatā€™s what I had before and Iā€™ve not got any reason to stop using it. (Itā€™s now the only bit of JS that gets applied sitewide).

Dates šŸ“…

I made use of luxon to create a couple of date filters, for blog posts and the RSS feed.

readableDate:  (dateObj)  =>  {
    return DateTime.fromJSDate(dateObj,  {zone:  'gmt'}).toLocaleString(DateTime.DATE_FULL);
},
timeStamp:  (dateObj)  =>  {
    return DateTime.fromJSDate(dateObj,  {zone:  'gmt'}).toFormat('yyyy-LL-dd HH:mm:ss');
}

But the built in date filters are also pretty good, using the syntax from strftime like {{ post.date | date: "%B %Y" }} (which outputs April 2020). In fact, I canā€™t 100% remember why I created those filters when I could possibly have used those šŸ¤”.

Still not found a simple way of adding the day suffix (st/nd/rd/th) for the readable date though.

Console.logging šŸŒ“

Console log is a pretty recent addition. As a unashamed fan of debugging in this way, I appreciated that this is now built in. You can use it for any bit of data. I mostly found it useful to check what was making it into collections, but tbh itā€™s a bit of a messy way to work, as the log gets output alongside all the build notifications when running --serve in the terminal.

{{ featuredImage | log }}

Screenshot of the 11ty build output

Building collections šŸ“¦

Thereā€™s a lot written about creating collections, and handling blog posts was pretty straightforward. I liked the approach I found in Kristoffā€™s code of having the main post collection, and then other collections where all posts are grouped by tag and/or category. Itā€™s a little more legwork, but made more sense to me when it came to rendering archive pages.

Beyond blog posts, I did find it a little tricky to come up with a way of managing my image galleries. I wanted to have a collection of all my images that I could loop through by tag, without creating pages for them all (because I had hundreds and it was taking a while to build, not to mention I didnā€™t ever link to them) ā€“ thatā€™s where permalink: false in the frontmatter came in.

But then I wanted to have pages created only for photos with a particular tag (ā€˜printā€™, indicating you can buy a copy of it). To do that, I needed to create pages from a subset of the main collection, by using ---js instead of vanilla frontmatter, which allows me to apply a before callback. You can see it on my print.liquid file.

...
pagination: {
    data: "collections.allPhotos",
    size: 1,
    alias: "photo",
    before: function(data) {
      return data.filter(function(item){
          return item.tags.includes("print");
      })
    }
}

What Iā€™ve ended up with does feel a little convoluted, with 5 collections on the go for my photography (as well as all the blog collections). But it works, and itā€™s quick to build, so thatā€™ll do for now.

Content management šŸ—ƒ

Iā€™m not really using a CMS at the moment. Plenty of talk out there about using Netlify CMS but I havenā€™t made any move in that direction yet. What I am using is stackedit to assist with my markdown where needed (Iā€™ve never relly gelled with it, though itā€™s growing on me), and handcoded blocks for my liquid pages such as about me.

Iā€™ve also retained a fragment of my WordPress set up to manage my photos. All the details for them are curated in WP then made available as a json file which I manually copy across to the 11ty _data directory as photos.json. That then gets picked up by front matter pagination to create the collections I mentioned before.

Itā€™s fine. I donā€™t intend to change content much, but if it becomes unmanageable, Iā€™ll look into a different approach.

Deploying to your own hosting šŸš€

Contrary to general concensus, Iā€™m not using Netlify to host my site. I already have my hosting and domain and SSL configured and paid for, so I wanted to stick with it. Frustratingly, I couldnā€™t find much in the way of documentation to go along with how to deploy to anywhere other than Netlify (or GitHub Pages), but I got there in the end.

Iā€™m making use of GitHub Actions, having created a workflow file to watch for any commits to the main branch. It runs the 11ty build command then pushes it to a built branch thanks to an action intended for use with GitHub pages, and thatā€™s where I was stuck for a while. Iā€™ve used a GitHub workflow making use of SFTP before, but couldnā€™t get it to work in this particular set up.

Eventually, I opted for a middleman (šŸ‘‹ Buddy) which fills the CI gap with a pipeline that watches my built branch and then finally deploys via SFTP. It sounds more complicated than it is, and yes ā€“ itā€™s FTP šŸ‘“šŸ», but Iā€™m really pleased with how quick and seamless it actually is.

My Buddy configuration

Performance šŸŽ

Everyone raves about the performance gains that can come from using a static site generator like 11ty, and rightly so. The improvements can be dramatic and theyā€™re of course very welcome. #perfmatters.

Butā€¦ Iā€™d actually worked really hard to get my old site performing well. Yeah it was WordPress but that doesnā€™t automatically mean ā€˜slowā€™. So when it came to testing this new site, the improvements were so minimal ā€“ and the issues that existed were of course the same. I would hope that it goes without saying but ā€œjust usingā€ 11ty isnā€™t going to immediately grant your Lighthouse wishes.

There are other benefits away from the digits at the top, but itā€™s those numbers that get you on the 11ty Leaderboards [I still donā€™t know how? I want to be on there!], and purely on those numbers, my WP site more than held its own.

Old performance scores compared to new performance scores
Sidenote: on my old WP site, I made use of some randomly generated SVG wave transitions to slightly mask the minimal loading times between pages, and while I still love the effect, I thought it probably unnecessary with this new site so theyā€™re gone (I may still bring them back šŸ‘€ they were pretty).

Liquid šŸ’¦

I used Liquid for my template files (primarily out of respect to Liquid Swords ā€“ word to GZA ā€“ and Iā€™m loving how easy it is to use. But most people seem to use Nunjucks, so some of the syntax differences caught me out, such asā€¦

šŸ¤” set in Nunjucks == capture in Liquid (src).

šŸ¤” In Liquid, elseif is actually written elsif because of course, why would it need that second ā€˜eā€™(?) šŸ™„ (src).

šŸ¤” Within a forloop, to use a counter, I tried {{ assign i++ }} but it didnā€™t do anything. The docs recommend using increment: {{ increment myVar }} but spits it out on the page. So I ended up finding forloop.index and using {% assign num = forloop.index %} in order to track the iteration.

šŸ¤” or doesnā€™t seem to work in liquid templates, despite it being referenced widely. Plenty of examples of 11ty code show things like: <title>{{title or metadata.title }}</title> but I had to change it to messier looking if statements to get it to work:
<title>{% if title %}{{title}}{% else %}{{metadata.title }}{%endif%}</title>

ā€¦ and finally šŸ

A few little things that made me pull my hair out before realising they were simple fixes.

šŸ‘‰ When using a 11ty.js data file (like I am to generate different image sizes), you need to add them to the templateFormats in the eleventy config or they donā€™t get picked up. This took me a LONG time to realise: templateFormats: ["md","html","liquid","11ty.js"]

šŸ‘‰ If youā€™ve got tags being set in Frontmatter and a directory data file, and want to merge them, use deep merge: eleventyConfig.setDataDeepMerge(true)


And thatā€™s it.

If Iā€™m honest, I didnā€™t really have anything against the old WordPress site other than the ongoing fear of losing everything whenever there was an update šŸ˜¬. I managed to do pretty much anything I needed to do, and I was comfortable developing with it. But, Iā€™d had that site for 14 years and when you add on and add on to something for that longā€¦ It felt right to change it up and start from fresh (albeit, with the same design and some of the same CSS). Itā€™s good to get out of your comfort zone. 11ty ticks a lot of front-end developer boxes for me, and Iā€™m looking forward to working with it some more šŸŽ‰.

Some things left to do / look at

  • Related posts
  • Blog search
  • Service workers
  • Fonts that arenā€™t on Google
  • CMS
  • Colour theme switcher
  • Responsive imagesā€¦ images with captions
  • Actual pagination on the archive pages
  • Various bits of content to migrate / create
    1. Blog posts back to 2008
    2. A lot more photo collections
    3. TIL
    4. Work portfolio

Pop back soon to see the progress šŸ‘‹