11ty: Intro & Live Demo with a JavaScript Static Site Generator

jamstack-snipcart-blog

In a rush? Skip to tutorial or live demo.

Static site generators are like Netflix series.

Just when you thought you'd seen them all, you find another one.

And they all look great!

stranger-things

Listen, I'm not complaining here. We have lots of fun trying them out! And that's what I'm here to do, once again.

The SSG on the menu today? 11ty (or Eleventy) an up-and-coming JavaScript static site generator.

Here's what I'll cover in the JAMstack tutorial below:

  1. Setting up a web store with Eleventy.
  2. Creating products and injecting a cart.
  3. Adding code highlighting to blog posts!
  4. Outputting a JSON file for products.

But let's get more familiar with 11ty first.

What is Eleventy?

11ty-javascript-static-site-generator

Well, that picture kind of burned my line, but still:

Eleventy is simple static site generator written in JavaScript.

You may not have heard of it yet, mostly due to its young age. It was released at the dawn of 2018 but is quickly making a name for itself on the SSG scene.

11ty-growth

800 & counting GitHub stars in less than a year! [Source]

How does this little wonder work? Simply by transforming a directory of templates of varying types into HTML. Eleventy doesn't take the words "varying types" lightly—one of its main features being the flexibility of its templating system.

It supports:

  • HTML
  • Markdown
  • Liquid
  • Nunjucks
  • Handlebars
  • Mustache
  • EJS
  • Haml
  • Pug
  • ES2015

It presents itself as a Jekyll alternative, and, from what I've seen so far, a pretty good one at that. To quickly start a project with no configuration, 11ty is probably even more efficient than its predecessor.

Then again, Jekyll still has a broader plugins ecosystem and will probably be easier to scale if that's what you need. It's not to say that 11ty won't catch up as it grows more mature, though.

Some developers are actually making the migration, confirming that it might be worth taking the time to compare both options.

Sidenote: read this to get started with Jekyll.

Intrigued yet? Let's dive into more technical stuff.

Read more about Eleventy in the words of its creator here.

11ty technical tutorial: Crafting a small e-commerce app

11ty-tutorial

The first thing to understand is that 11ty is driven by templates files. One file in your input directory equals one page on your website. Want a blog article? Add a .md and set a layout. A listing of these posts? Add a template file in which you iterate over the list of posts.

Let's dive into the specifics by building a small app with blogging and e-commerce capabilities.

Prerequisites

  • A Node.js installation.
  • Basic knowledge of npm packages.

1. Installing Eleventy

Start by creating an empty repository with a package.json.

I chose to install Eleventy on my project's node_modules folder instead of globally by running the command:

npm install --save-dev @11ty/eleventy

That way you can call the commands using npm start in development and npm run build for your production build by adding these lines to package.json:

"scripts": {
    "start": "eleventy --serve --watch",
    "build": "eleventy"
  },

First thing first, you need a home page. You can use ejs templating for that:

---
layout: default.ejs
permalink: /
---
<section>
    <h2>Welcome to our demo store!</h2>
    <p>…</p>
</section>
<section>
    <h2>
        <a href="/products/">Our Products</a>
    </h2>
    <ul>
        <% collections.products.slice(0, 4).map((product) => { %>
            <li>
                <a href="<%= item.url %>">
                    <img src="<%= product.data.image %>" alt="<%= product.data.name %>" />
                    <p><%= product.data.name %></p>
                </a>
            </li>
        <% }) %>
    </ul>
</section>

Notice the YAML front matter here? If you're familiar with Jekyll, it's the same approach. You can add a front matter to any file to define some parameters or new variables.

Most important, layout will define a parent template loaded from the _includes folder. Code reuse FTW!

The main layout is simple: a header, a footer and the cart's required elements:

---
title:
---
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><% if(title){ %><%= title %> – <% } %>A Snipcart and 11ty demo</title>
    <link rel="stylesheet" type="text/css" href="/assets/site.css" />

    <!-- Snipcart's required files -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
    <!-- Get your API Key from the Dashboard -->
    <script src="https://cdn.snipcart.com/scripts/2.0/snipcart.js" id="snipcart" data-api-key="PUBLIC_API_KEY"></script>
    <link href="https://cdn.snipcart.com/themes/2.0/base/snipcart.min.css" type="text/css" rel="stylesheet" />
</head>
<body class="main">
    <header>
        <% if(page.url == '/'){ %><h1 class="title"><% } else { %><p class="title"><% } %>
            <a href="/">Demo store</a>
        <% if(page.url == '/'){ %></h1><% } else { %></p><% } %>
    </header>
    <section class="content">
        <%- content %>
    </section>
    <footer>
        <!-- … -->
    </footer>
</body>
</html>

Add an empty title in the front matter to make sure the variable is always defined. page has information on the current page like the URL and its front matter in page.data.

The variable content has all the generated HTML of the child template or page.

2. Creating the store

Now to have a list of products, create a bunch of markdown files in a _products folder using the following format:

---
name: My Awesome Product
price: 59.99
image: /assets/products/product.svg
---

You can create collections automatically in 11ty by adding tags to your files' front matter, but here I chose to declare my products collection from the .eleventy.js config file:

module.exports = (eleventyConfig) => {

    eleventyConfig.addCollection("products", (collection) => {
        return collection.getFilteredByGlob("_products/**/*.md");
    });

    return {
        templateFormats: [
            "md",
            "ejs",
            "css",
            "svg",
            "png",
        ],
        passthroughFileCopy: true,
    };
};

By using getFilteredByGlob("_products/**/*.md"), the products collection is composed of every .md file in the _products folder.

In templateFormats, you define every template engine that will be used for input files (i.e., files that will be processed by 11ty), included layouts or partials aren’t affected by this configuration.

Add other types of files to this list with the option passthroughFileCopy so that static files get copied to the output directory as well.

Finally, for individual product pages, we could set the layout and permalink parameter on every markdown files, or you can use a directory data file which is a JSON file named the same as its parent directory.

In this case, it's in _products/_products.json:

{
    "layout": "product.njk",
    "permalink": "/products/{{ name | slug }}/"
}

I've used Nunjucks templating for that one.

---
layout: default.ejs
---
<section>
    <h1>{{name}}</h1>
    <img src="{{image}}" alt="{{name}}" />
    <p>
        <button class="snipcart-add-item"
                data-item-id="11ty-{{page.fileSlug}}"
                data-item-name="{{name}}"
                data-item-price="{{price}}"
                data-item-image="{{config.siteUrl}}{{image}}"
                data-item-url="{{config.siteUrl}}{{page.url}}">
            Buy for {{price}}$
        </button>
    </p>
</section>

The config variable is coming from a global data file in _data/config.json:

{
    "siteUrl": "https://snipcart-11ty-demo.netlify.com"
}

3. Exploring Eleventy's plugins

I could've stopped right there, but I wanted to play with 11ty's plugins.

Adding a few more lines of configuration and installing a new npm package (@11ty/eleventy-plugin-syntaxhighlight) lets you add syntax highlighting to the demo.

Register the plugin in .eleventy.js config file:

const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");

/* ... inside the callback: */
eleventyConfig.addPlugin(syntaxHighlight);

With it, I added highlighting of the Add to Cart button's code to the product's template:

{% highlight "markup" %}
<button class="snipcart-add-item"
        data-item-id="11ty-{{page.fileSlug}}"
        data-item-name="{{name}}"
        data-item-price="{{price}}"
        data-item-image="{{config.siteUrl}}{{image}}"
        data-item-url="{{config.siteUrl}}{{page.url}}">
    Buy for {{price}}$
</button>
{% endhighlight %}

Finally, I downloaded a CSS file for the languages I want to highlight from prism js.

4. Outputting a JSON file for products.

In some contexts, it's useful to use Snipcart's JSON crawler, but not every static site generator makes it easy to generate a listing of collections in two different formats.

With Eleventy, it's as easy as adding a new input template. And with a .ejs template, it's even easier by calling JSON.stringify:

---
permalink: /products.json
---
<%- JSON.stringify(

collections.products.map((product) => ({
    id: "11ty-" + product.fileSlug,
    name: product.data.name,
    price: product.data.price,
    image: config.siteUrl + product.data.image,
}))

) %>

Live demo & GitHub repo

11ty-demo

See live live demo here

See GitHub repo here

Closing thoughts

Honestly, I was stunned by how elegant and simple to use Eleventy is. In fact, the only issue I had was using .mustache templating for a pun with the demo's theme. Not an 11ty problem, it's just that mustache is, in my opinion, not the best templating engine within all the alternatives offered by 11ty.

After reading parts of the documentation, I quickly had a good understanding of Eleventy and managed to build the demo in about half a day.

An obvious improvement here would be to add proper pagination to the product listing. Also adding a CSS pre-processor to the build process would make things more maintainable in the long run.

Also changing the colors, according to our teammate Franck. ¯_(ツ)_/¯

All in all, I would strongly suggest Eleventy to anyone looking for a simple yet powerful static site generator.

I now believe it really is an excellent alternative to Jekyll. Have you tried both? If so, do you agree? Let's talk about this in the comments below!


If you've enjoyed this post, please take a second to share it on Twitter.

Suggested posts: