Why I migrated my website from GatsbyJs to Eleventy
Tags: webdev, webperf
Published: 2020-08-05 12:53:26

I announced it on twitter last week, and here you go with my new blog post about the migration from GatsbyJs to 11ty/Eleventy. In this post, I explain to you why I migrated to Eleventy, what were my challenges and how you can avoid them.

---

If you like this article, please share it, follow me, [check out my RSS feed](https://marcradziwill.com/blog/rss.xml) and [subscribe to my newsletter](https://marcradziwill.com/newsletter/).

---

I read much about [Eleventy](https://www.11ty.dev/) the last month and its creator [Zach Leatherman](https://twitter.com/zachleat). I got curious as I saw that it needs zero client-side JavaScript.

> Eleventy is a simpler static site generator.

I try to have about 2 hours to one day a week where I try something new for education purposes. So I decided to try out Eleventy. I thought I could use it for my clients who only need a static landing page. I ended up migrating my whole website from GatsbyJs to 11ty.

## Tldr;

In this post, I show you why I migrated my website from GatsbyJs to Eleventy, what my challenges were and how you can avoid them. This post tries to outline the main advantages and disadvantages of Eleventy compared to GatsbyJs.

## Table of Contents

1. [What is Eleventy, and how does it work?](#eleventy)
  - [Template engines](#engines)
  - [Layouts](#layouts)
  - [Data Cascade](#data)
  - [Configuration](#configuration)
  - [Filters and Shortcodes](#filters)
2. [What is GatsbyJs, and how does it work?](#gatsbyjs)
3. [Why I struggled with GatsbyJs?](#struggle)
4. [My migration from GatsbyJs to Eleventy](#migration)
  - [My Image pain](#image)
    1. [Async image shortcode](#async)
5. [Is GatsbyJs a wrong choice?](#choice)
6. [Next steps](#next)
7. [Conclusion](#conclusion)

<hr />

## What is Eleventy, and how does it work? <div id="eleventy"></div>

Eleventy is a simple static site generator. That's it. It uses data and templates to generate HTML files you can upload to your hoster and serve them statically.

That is almost all you need to say about Eleventy. It is fascinatingly simple. Of course, we are going to dive deeper, and there is a lot more to mention, but the paragraph above describes 11ty in its core concept.

You start with Eleventy by choosing your preferred template engine. Probably the easiest is **[Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet)**. The following file shows a simple Markdown example for eleventy. Eleventy uses your file structure to generate HTML files.

`index.md`

```html
# My custom headline
```

Now let's run this simple example with: `npx @11ty/eleventy`. This command will generate a `_site` directory with an `index.html` containing the following markup.

`_site/index.html`

```html
<h1>My custom headline</h1>
```

What if you have more complex content that you can not display with markdown? Eleventy supports multiple template engines you can use.

### Template engines <div id="engines"></div>

- HTML \*.html
- Markdown \*.md
- JavaScript \*.11ty.js
- Liquid \*.liquid
- Nunjucks \*.njk
- Handlebars \*.hbs
- Mustache \*.mustache
- EJS \*.ejs
- Haml \*.haml
- Pug \*.pug
- JavaScript Template Literals \*.jstl

### Layouts <div id="layouts"></div>

While I tried out some of the templates, I recognized how fast I was able to create pages. I went with [Nunjucks](https://mozilla.github.io/nunjucks/) as it is very flexible. In no time a created pages and wrapped them with layouts. [Layouts](https://www.11ty.dev/docs/layouts/) are some unique templates in Eleventy you can refer from your pages. A layout for our example above could look like this:

`index.md`

```html
---
layout: main.njk
---

# My custom headline
```

`_includes/main.njk`

```html
---
title: My tutorial
---

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{ title }}</title>
  </head>
  <body>
    ... contents
  </body>
</html>
```

The layout in the `_includes` folder wraps the `index.md` page and outputs all
to the `_sites` folder.

### Data Cascade <div id="data"></div>

Working with data in eleventy is very simple. The data is merged from different sources before the engine renders the template. The data-merging is called Data Cascade and it contains the following parts.

1. [Computed Data](https://www.11ty.dev/docs/data-computed/)
2. [Front Matter Data in a Template](https://www.11ty.dev/docs/data-frontmatter/)
3. [Front Matter Data in Layouts](https://www.11ty.dev/docs/layouts/#front-matter-data-in-layouts)
4. [Template Data Files](https://www.11ty.dev/docs/data-template-dir/)
5. [Directory Data Files (and ascending Parent Directories)](https://www.11ty.dev/docs/data-template-dir/)
6. [Global Data Files](https://www.11ty.dev/docs/data-global/)

### Configuration <div id="configuration"></div>

You can use Eleventy without any configuration, but if you want to add some additional features or use a plugin you can add it to your `.eleventy.js` file and expand the functionality.

### Filters and Shortcodes <div id="filters"></div>

With [filters](https://www.11ty.dev/docs/filters/) and [shortcodes](https://www.11ty.dev/docs/shortcodes/) you can expand the template functionality. For example, you could define a shortcode that renders a button or a filter that formats a date.

## What is GatsbyJs, and how does it work? <div id="gatsbyjs"></div>

Gatsby is a static site generator as well. It is React-based and uses GraphQL. It is preconfigured and has used many tools you need to implement usually by yourself like webpack or a service worker. Gatsby uses the `pages` directory to create each page for your site. It has a huge community and a wide range of plugins with whom you can expand the functionality of your website.

It builds its data-layer with GraphQL and transforms your JSON, Markdown files or external API data to nodes which you can use and query in your templates. You can start very fast with your first GatsbyJs site with the [Quick Start](https://www.gatsbyjs.org/docs/quick-start/) on the GatsbyJs documentation site. Last year I did a web performance experiment and chose GatsbyJs because they promise a `blazing fast` site.

## Why I struggled with GatsbyJs? <div id="struggle"></div>

GatsbyJs is an excellent framework, and while many developers do not like ReactJs because of its performance bottlenecks, I found myself happy with my developing experience. If you didn't read through my [web performance experiement](https://marcradziwill.com/blog/my-web-performance-experiment-a-speed-field-trial/) I had some trouble last November after I launched my site. I knew I would have some performance issues to face, but I wasn't expecting such big ones.

My site was slow even while I used blazing-fast GatsbyJs. Of course, there were many issues which weren't related to Gatsby, but Gatsby was part of the problem. I spend hours over hours to optimize my site. Even though I know what I am doing, it was quite hard and took much longer than developing my website from scratch.

Gatsby needs much client-side JavaScript to work. If you work on web performance, you know, it's good to reduce client-side JavaScript to a minimum. If you are not into that topic check out how the browser parses JavaScript in my blog post about [JavaScript high performance](https://marcradziwill.com/blog/mastering-javascript-high-performance/) and especially my series about the [Business Value of Speed](https://marcradziwill.com/blog/business-value-of-speed-part-one-critical-rendering-path/) where I go through the sequence of steps the browser needs to process before it can paint a pixel.

I wanted to reduce the amount of JavaScript and was quite successful with a plugin called `gatsby-plugin-preact` it replaces React with Preact and cut the bundle files size of my `framework-[hash].js` from **~40kB** to **8.6kB**. That was great, but at the same time, it was a bit strange to erase the developing framework React at build time.

Nevertheless, I still struggled with all of my components. I did some research and found the plugin [gatsby-plugin-no-javascript](https://github.com/itmayziii/gatsby-plugin-no-javascript). It removes JavaScrpt at build time. I looked at the file size and was happy for a moment. Just for a moment because I knew it breaks my site. I'll tell you the truth. I wasn't able to reduce the amount of JS to proper file size. While I write this post, I still have **81.8kB** of JavaScript for the main app rendering my mostly static site.

Not to mention that I use lazy loading like a freak so additionally, I have 20 requests to JS files that are loaded later between **2kB** and **25kB**. You could say let's reduce the JavaScript of our components. I did this for hours. I deleted code, I changed libraries, and I removed functionality.

## My migration from GatsbyJs to Eleventy <div id="migration"></div>

Here comes Eleventy. At this time now, I migrated all my content to 11ty and implemented all interactions with JavaScript that I need. I now have one `main.js` of **2.2kB** and two bundles that are lazy-loaded with webpack on the pages that need them. One with **941 B** and one with **678 B**. I didn't remove any functionality.

The following table shows you the size of all MIME Types before and after the migration.

**Before**

| MIME Type |  Bytes  | Uncompressed |
| --------- | :-----: | -----------: |
| js        | 247,435 |      675,335 |
| image     | 59,606  |       59,606 |
| html      | 23,210  |       90,011 |
| other     | 22,530  |      110,534 |
| css       |  2,126  |        6,455 |

**After**

| MIME Type | Bytes  | Uncompressed |
| --------- | :----: | -----------: |
| font      | 65,552 |       65,552 |
| image     | 50,263 |       50,263 |
| js        | 21,590 |       52,300 |
| html      | 18,742 |       85,866 |
| css       | 3,231  |       10,972 |
| other     |  336   |        1,148 |

So where did I started? `index.js`. Nope. As my home page is an overview page, I started with my last blog post. I write my blog posts in markdown and use frontmatter in the head of the file for additional information. This blog post you currently reading looks like this:

```html
---
tags:
  - 'javascript'
  - 'webperformance'
layout: layouts/post.njk
article: true
title: 'Why I migrated my website from GatsbyJs to Eleventy'
date: '2020-08-05'
author: Marc Radziwill
seo:
  {
    follow: 'index,follow',
    title:
      'blog: Why I migrated my website from GatsbyJs to Eleventy - Marc
      Radziwill',
    description:
      'Eleventy is the new runner up in the JAMstack community. Read why I
      struggled with GatsbyJs and why I choose Eleventy for my website >>',
  }
categories: ['javascript', 'webperformance', 'business']
keywords:
  - webperformance
  - frameworks
image:
  {
    src: ./src/blog/business-value-of-speed-part-two-metrics-and-mapping-of-business-values/images/business-value-of-speed-part-two-metrics.jpg,
    alt: 'alt text',
    title: 'image title',
  }
readingDependencies: You must be at least a level beginner to make this happen
readerLevel: beginner
published: true
isTechArticle: true
---
```

These files have quite some information in it. In Gatsby, I needed to create NodeFields to query them. So for every entry, I did:

```js
createNodeField({
  name: 'title',
  node,
  value: node.frontmatter.title,
})
```

In Eleventy and due to their data cascade these values are already accessible in the template file for example if you loop through a [collection](https://www.11ty.dev/docs/collections/) of pages within the `data` Object like this:

```handlebars
{%- for page in collections.all -%}
  <h2>{{ page.data.title }}</h2>
{%- endfor -%}
```

After one hour, I migrated all my posts content without the images. I was convinced and decided to migrate my entire content. That was what I was looking for.

I continued by defining a layout for my base pages and my blog pages to serve different CSS and JS files. I built the main components and Nunjunk partials like the header and the footer, and I created some shortcode components like a button.

One week later, I am ready for my first test with [my site](https://eleventy-marcradziwill-com.vercel.app/) running completely on Eleventy. I was fast. Faster than I ever thought I could be re-writing my whole website. But I also was struggling with some parts.

### My Image pain <div id="image"></div>

Images were my biggest problem. My site contains a lot of header images that I want to render in a `picture`-tag with correct sizes and formats that the browser can decide which image it should download. Gatsby has a smooth plugin that handles images. For Eleventy, I chose the plugin `eleventy-img` and wrote a Nunjunk async shortcode to render the images before the blog content starts.

#### Async image shortcode <div id="async"></div>

```html
{% resImage theme.logo.src, "alt text", theme.logo.options, "title text" %}
```

That worked quite well for all Nunjunk templates, but I could not use them in my blog posts as they are based on markdown. For all Maordown images below the fold, I implemented a workaround. There might be a better solution, and it still feels a bit dirty, but for now, it works.

First I encounter all `img` tags with an eleventy transformer for all html files that are in my \_site folder. I use the npm package `basichtml` to make the content of each page selectable and query all images with `document.querySelectorAll('.post-content :not(picture) img[src]:not([srcset]):not([src$=".svg"])'`.

I loop through them and create a `picture`-tag with the help of my `resImage` async shortcode. The idea to this and some part of the code is from [Nicolas Hoizey - Eleventy Image Responsiver](https://github.com/nhoizey/eleventy-plugin-images-responsiver). After all, images are converted to a `picture`-tag with the correct source path; I use the `eleventy-plugin-lazyimages` to use native image loading for my pictures.

Additionally, I wanted to have my image files located where the blog post file is. This makes it easier for me to maintain them and to cross-post them to dev.to, for example.

So my file structure looks like this:

```js
blog
│───blog-post-1
│ └───images
│          image-1.png
│          image-2.png
│  index.md
```

`eleventy-plugin-lazyimages` wasn't able to locate the relative path from the blog post. I know that this usually should work, but I wasn't ready until now to make it work. For now, I use the absolute path starting with `./src/blog/blog-post-1/images/image-1.png`. This results in a confusing pah in my `_site` directory like `_site/assets/images/src/blog/blog-post-1/images/image-1/9cd1e3v7-300.jpeg`. That is something I need to improve. **I would recommend using the image folder for all images if you are facing the same issues**.

## Is GatsbyJs a wrong choice? <div id="choice"></div>

No, not at all. Gatsby was a wrong choice for me, having my requirements and wanting to have a site without much JavaScript, but GatsbyJs provides some great features. If you are struggling with JavaScript, you should consider migrating to Eleventy as well. Eleventy is fast and flexible not only while you are developing but also during runtime.

## Next steps<div id="next"></div>

I migrated my website in one week from Gatsby to Eleventy. So for the moment, I am delighted and satisfied. For sure there are still some open tasks which I will work on the next weeks. If you are thinking about migration, I recommend you to try out Eleventy. It is fun, and you will love it! ♥️

Check out my current [result](https://eleventy-marcradziwill-com.vercel.app/).

## Conclusion <div id="conclusion"></div>

In this post, I tried to show you why I migrated my website from GatsbyJs to Eleventy and why I think this is important for a fast site.

If you like this article, smile for a moment, share it, follow me, [check out my RSS feed](https://marcradziwill.com/blog/rss.xml) and [subscribe to my newsletter](https://marcradziwill.com/newsletter/).

Cheers Marc