Last year I published a blog post about why I moved my site from gatsby to Eleventy. I was struggling with Gatsby because of the enormous JavaScript overhead. If you are new to Eleventy, I recommend you to read my first post before continuing.

Now it’s been quite a year since I switched. I wrote down what my experience with Eleventy is so far. How I use it and how it fits into my blogging life as a web performance consultant.

Introduction

I came to Eleventy because of all the pain I had with GatsbyJs. As a performance addict, it’s a huge pain. You can invest a whole week into optimizing React components. You add Preact and minimize the main thread work with adaptive loading. For me, this ended in developing every component twice.

Eleventy, on the other hand, is super simple, and it has no client-side overhead. If you start, there is no JavaScript. That still makes me smile when I think about that moment— pure HTML and CSS. And to be honest, after one year, I still only need 4kb of JavaScript.

I know my page isn’t very complex, but I still had some requirements for my set-up.

  • Have my website in English and German. I needed a multilingual concept.
  • Add an RSS feed for my followers that don’t like newsletters
  • Create components that I can reuse on multiple sites
  • My website should be sustainable
  • Syntax highlighting was a must
  • Serve critical assets first, lazy load the main CSS later
  • Serve images in modern formats fast and don’t blow up the build time
  • Serve a readable and inclusive blog
  • Integrate webmentions
  • Integrate my Review Forest reviews.
  • Run interactive and dynamic tests or quizzes
  • Use different data sources for some components

Development recap

I thought that list might be a challenge for a static site generator. To reveal that in advance, I was very wrong. The learning curve is very steep with Eleventy. I had my first page in no time, and within an hour or two, I created pages with reusable components and my RSS feed. Like I described in my first post, I migrated my website in one week. That means it took one week to implement 66+ % of my requirements. I never had such a time-saving benefit during the development before.

Additionally, I was able to complete one part with a bit more complexity. Rending images in a modern format is not a big deal in Eleventy. I struggled a bit with images because I wanted to set the sizes for each image at the image itself and not globally. Image handling with the elevent-img plugin is like a dream coming true. Especially if you define global sizes for every image.

You can write a transformer and let it create your images. The transformer applies the picture-tags after Eleventy generated all HTML files. That transformer works fine for all my blog post images below the fold. They all have the same size for mobile and desktop viewports.

Hero and text-teaser images are different on my site. I wanted to set the image size right in place. To do so, I added a custom async nunjucks shortcode. It renders a picture-tag and expects an object of meta information. This options object includes the following information:

  • title: String
  • alt: String
  • widths: Array
  • blurry: Boolean
  • lazy: Boolean

With this information, I can render the picture-tag. I add a data attribute data-processed and check this flag for each transformation. Thereby I avoid rendering images twice with the shortcode and the transformer. Working on the images took a bit longer because debugging is quite hard. I had to rebuild the whole project for each change. On top of eleventy-img I use some parts from the Google eleventy-high-performance-blog to create blurry placeholders.

Images were the only part of the 66% of my requirements from that one week that took me longer to implement. Thus you can imagine how fast I finished the other parts.

  • [x] Have my website in English and German. I needed a multilingual concept.
  • [x] Add an RSS feed for my followers that don’t like newsletters
  • [x] Create components that I can reuse on multiple sites
  • [x] My website should be sustainable
  • [x] Syntax highlighting was a must
  • [x] Serve critical assets first, lazy load the main CSS later
  • [x] Serve images in modern formats fast and don’t blow up the build time
  • [x] Serve a readable and inclusive blog
  • [ ] Integrate webmentions
  • [ ] Integrate my Review Forest reviews.
  • [ ] Run interactive and dynamic tests or quizzes

My daily work with Eleventy

That is the basis of my Eleventy site. The three remaining tasks give better examples for my daily work with Eleventy.

I try to enrich my website and my blog post content with helpful information. At the same time, I respect my users’ privacy on a very high level.

Consequently, I integrated as many resources as possible into the build process. You won’t find any Twitter or Instagram embeds on my website that are talking back home.

Webmentions

Encouraging my readers to engage with my content is still an important part of a successful page. To do so, I integrate my webmentions on each blog post. Setting up the webmentions with webmention.io and birdy is not hard. After that, you can receive webmentions during the build time.

If you already have some reposts or shares from others, you will get them with the API call below. If you start from the green, send yourself the first webmention by replacing the domain name. You can also send me a webmention if you want.

async function fetchWebmentions() {

  let url = `webmentionapi`;
  try {
    return Cache(url, {
      duration: '2d',
      type: 'json',
    });
  } catch (e) {
    return null;
  }
}
  • [x] Integrate webmentions
  • [ ] Integrate my Review Forest reviews.
  • [ ] Run interactive and dynamic tests or quizzes

Reviews

Reviews are essential for any business online and offline. I do have local customers and online ones. Google reviews are still playing a big part, even if you don’t have hundreds of them. I’m using a service called Review forest to receive more reviews.

Simultaneously, I would like to contribute a small part to sustainability. For each review I get, I pay 5 € and Review Forest plants a tree. This kind of review gathering in my mind is contemporary.

They do have a widget that you can integrate on your website with a script tag. You can imagine that this is not the whole story. The script tag does a bunch of things on the website. It loads a 1.7 MB (gzip 😱) JavaScript file and makes five requests to the API. I don’t know why they need all this to print a number, but that might be their secret forever.

Reviews are crucial for any business, and the service of Review Forest promotes sustainable thinking. Therefore I still want to add this service to my site. With Eleventy, I’m fetching the reviews on build time and add them directly into my page in the footer. That is a one-liner in the HTML and a small function to fetch the results. The best part is that it adds 0 MB of JavaScript to my site.

async function fetchReviews() {
  try {
    return Cache('reviewforestapiurl', {
      duration: '2d',
      type: 'json',
    });
  } catch (e) {
    return null;
  }
}
<div class="tb-google-badge">
    <a href="https://reviewforest.org/marcradziwill" target="_blank" rel="noopener">
        <div class="tb-trees-badge bottom-left">
            <!-- ... -->
            <div class="tb-tree-widget__info">
                <div class="tb-tree-widget__amount">{{reviews.count}}</div>
                <!-- ... -->
            </div>
        </div>
    </a>
</div>

These lines of code prevent me from adding a bunch of critical assets. I still use the service from Review Forest. With this, I can check off the next requirement.

Quiz

I always wanted to do a quiz. I decided to add a web performance quiz. I know doing a quiz is no rocket science, but it’s easier to do it with Eleventy. You can use the folder structure to create pages. Besides that, you can use this fact to create a collection.

The collection contains pages you can use somewhere else on your website. For my web performance quiz, I’m using the following folder structure.

project
│
└───web-performance-quiz
│   │   index.jk
│   │   questions.jk
│   │   result.jk
│   │
│   └───quiz
│       │   01.MD
│       │   02.MD
│       │   ...
│       │   31.MD
│       │   quiz.json

Each markdown file represents one question with the answers.

---
title: 'Who is solely responsible for web performance?'
date: '2020-10-01'
answers: [
  {
    text: 'Developers'
  },
  {
    text: 'Anybody who ships. Developers, DevOps, SEO-Experts, Marketeers, etc.',
    correct: true,
    description: ''
  },
  {
    text: 'Product Owner'
  },
  {
    text: 'Project Manager'
  }
]
---

Third-party is content that not your domain serves to the users. It is content that you don’t control, and it is not your server that delivers the content to the user.

Examples of third-parties:

- Analytics
- Advertising
- Tag Managers
- JavaScript from CDNs
- Social sharing buttons
- Video player embeds
- Chat services
- A/B testing scripts for experiments
- Performance measurements

I use these files as data silos for the quiz overview and the result page. Furthermore, I they help me to create pages with an SEO-friendly URL for each question. To prevent Eleventy from creating routes directly, I added the quiz.json file to the root. permalink set to false avoids the page generation with the URL https://marcradziwill.com/web-performace-quiz/quiz/01. The other settings help me create a collection (tags) and exclude the URLs from the site sitemap. locale is a custom setting that helps me identify if the current page is in English or German.

quiz.json

{
  "tags": ["webperformancequiz"],
  "locale": "en",
  "excludeFromSitemap": true,
  "permalink": false
}

The fun part of this happens in questions.njk. This file creates all pages for each question. The file might seem complex, but when you have a closer look, it is straightforward.

---js
{
pagination: {
  data: 'collections.webperformancequiz',
  alias: 'question',
  size: 1,
},
permalink: 'web-performance-quiz/{{ question.data.title | slug }}/'
}
---
<!-- progress-bar -->
<div class="..." >
  <h1>{{question.data.title}}</h1>
  {{ question.templateContent | safe }}
  {% for answer in question.data.answers %}
      <!-- loop -->
  {% endfor %}
  <!-- pagination buttons -->
</div>

I use the pagination functionality to create a collection of pages. As webperformancequiz is the tag for the questions, Eleventy adds all to the object. I can access the alias in the njk-file below the configuration, and then I have my quiz with individual pages.

  • [x] Integrate webmentions
  • [x] Integrate my Review Forest reviews.
  • [x] Run interactive and dynamic tests or quizzes

All in all, Eleventy gives me a range of possibilities to enrich my site with valuable content. Besides that, I still have the benefit of a static site with all its advantages. The latest feature I added is a recent posts widget for my blog pages. Sometimes my posts do overlap in terms of content, and I want to give that information to my readers.

In the .eleventy.js configuration file, you can define a markdownTemplateEngine setting. I setted it to markdownTemplateEngine: 'njk'. Before Eleventy renders the .md-file, it prerenders it with nunjucks. After that, it lets me add my recent post widget without any additional work.

Build time

The build time is the only bottleneck I found. Building a large number of pages takes quite some time on the initial run. They are working on this topic as the time of rebuilds significantly dropped after the last update. The advantages far outweigh the disadvantages of Eleventy. I use a simple workaround if I work on fetching remote data like webmentions. As the request is a Node.js file, I run the file without Eleventy and add it later.

Web Components

Web Components are custom components with names like <my-web-component>. This technology allows you to create reusable elements. Web components encapsulate the functionality away from the rest of the code. One popular example of Web Components with Eleventy is web.dev. Web Components might be the solution for you if you need a bit more logic.

Conclusion

I love the work with Eleventy, and I can not recommend it enough. Eleventy was not tailored to sites that rely more on component-based development. Web Components and the eleventy-vue-plugin changed that. Zach describes the usage and the reason for this crossover in this post. Only for websites with a SPA character Eleventy might not be the base choice.

So Eleventy is not a solution for everything, and it has its bottlenecks. But first, without being a maintainer, that is not the aim of Eleventy. Secondly, it provides all the features you need to develop a fast website. Finally, it gives you the flexibility to meet individual requirements.

Cheers Marc