Generate a Static Website with 11ty (Eleventy)

This post will describe how to create a simple website with the NodeJS-based static site generator (SSG), 11ty (Eleventy). According to this article. 11ty is the 3rd fastest SSG.

I’ve tried Hugo. It’s definitely fast and easy to install. But, I found that it wasn’t flexible in how it could be used and not as intuitive as Jekyll and 11ty. I’ve also tried Jekyll. It was a bit tricky to install on Windows. It would sometimes get stuck or not detect file changes and therefore not build and reload them in the browser. LiveReload didn’t work, for me at least. 11ty was super easy to install, as you’ll see below. The build time definitely goes up with the number of pages. It’s super flexible and customizable and intuitive to use. And, it supports a bunch of templating engines like HandlebarsJS, Liquid, Nunjucks, EJS, and even plain JavaScript.

Here’s a step-by-step guide to getting started with 11ty to build a simple website from scratch. This tutorial was done on Windows.

Install the latest version of Node JS

https://nodejs.org/

Verify Node JS installation

If you’re on Windows, open the Command Prompt and run node -v to check the version of Node installed.

Optionally, install the latest version of PowerShell

If you’re on Windows, you can use the default command prompt. Or, you can use PowerShell. If you don’t have the latest version of PowerShell, you can download it from here.

Instead of the default command prompt (cmd.exe), I’m going to use PowerShell. Open PowerShell and run node -v to check your version of Node installed. If PowerShell doesn’t find Node, then check that Node is in your path.

Open System Properties

Click Environment Variables

Under System variables, click Path.

If there is no path to the nodejs folder, add it as the last item. 

If it exists but comes before the PowerShell path, then move it down below the PowerShell path.

Close and reopen PowerShell and rerun node -v.

Create website folder

mkdir eleventy-poc
cd eleventy-poc

Create a package.json

npm init -y

Install eleventy and save it in package.json

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

Run eleventy (verify if runs)

npx @11ty/eleventy

Install HTML 5 boilerplate

Go to https://html5boilerplate.com/ and download the latest version.

Extract and copy all files to the website folder (eleventy-poc) EXCEPT for package.json and package-lock.json. You don’t want to overwrite the 11ty dependencies that were already saved there.

Create an _includes and _layouts folder

Add the following code to 

  • Specify folders that will be copied to the build output folder
  • Specify which folders contain include and layout files

Include files are reusable code snippets like components, e.g. header, footer, etc.

Layout files are files that determine a page’s layout. Layout files can wrap other layout files.

module.exports = function(eleventyConfig) {
    // Output directory: _site
 
    // Copy `css/` to `_site/css`, etc
    eleventyConfig.addPassthroughCopy("css");
    eleventyConfig.addPassthroughCopy("img");
    eleventyConfig.addPassthroughCopy("js");
 
    return {
        dir: {
        // ⚠️ These values are both relative to your input directory.
        includes: "_includes",
        layouts: "_layouts"
        }
    }
};

Update links

The doc folder contains instructions on how to use HTML5 Boilerplate. You may delete it if you want. If you keep it, update the links (search and replace) TOC.md to /doc/TOC/ so that when the markdown files are built as HTML files, the TOC links will work.

Also, in TOC.md, let’s do the same with the local links, e.g. 

  • “usage.md” becomes “/doc/usage/” 
  • “faq.md” becomes “/doc/faq/”
  • etc

Re-run Eleventy when you save 

npx @11ty/eleventy --serve

This runs a local web server and loads BrowserSync to auto-reload the browser when you save. 

Notice in the output below that a _site folder was created containing 10 HTML files that were built and 14 files were copied. The files that were copied were from the css, js, and img folders as specified in the config file.

The output folder structure looks like this

We can browse to http://localhost:8080/ and see the home page. If we go to http://localhost:8080/doc/TOC/, we’ll see the HMTL5 Boilerplate documentation in HTML format (converted from Markdown).

Create a default layout

At this point, we have our boilerplate home page (index.html) file.

Let’s create a header, footer, and sidebar containing nav links, and a main content area, like most websites have.

<header>
    <p class="logo">ABC Company</p>
</header>
  <div class="content-wrapper">
    <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about/">About</a></li>
        </ul>
    </nav>
    <main>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore, velit provident sed cum repellendus repudiandae ut mollitia animi. Voluptates expedita corporis pariatur sed quam. Ducimus fugiat quos eum aut eaque?</p>
    </main>
</div>
<footer>
    Copyright 2022
</footer>

I’ll also add some basic CSS to main.css to style the page.

/* ==========================================================================
   Author's custom styles
   ========================================================================== */
header,
footer {
    padding: 1em;
    background-color: #70a9dc;
}
.logo {
  font-size: 2em;
}
.content-wrapper {
  display: flex;
  min-height: 50vh;
}
main {
  padding: 1em;
}
nav {
  background-color: #efefef;
  padding-right: 3em;
}

Now, 11ty (BrowserSync) will auto-reload the page and we’ll see

Let’s create a layout from this index.html page.

  • Copy index.html to _layouts/default.njk (this will be a Nunjucks template)

In _layouts/default.njk

  • Prefix all relative paths with “/”, e.g.
    • css/normalize.css becomes /css/normalize.css
    • js/main.js becomes /js/main.js
  • Copy the <head> code block to _includes/head.html
  • Copy the <header> code block to _includes/header.html
  • Copy the <nav> code block to _includes/nav.html
  • Copy the <footer> code block to _includes/footer.html
  • Copy all of the <script> tags to _includes/scripts.html
  • Replace the <head> code block with {% include "head.html" %} to include the head code
  • Replace the <header> code block with {% include "header.html" %} to include the header code
  • Replace the <nav> code block with {% include "nav.html" %} to include the nav code
  • Replace the <footer> code block with {% include "footer.html" %} to include the footer code
  • Replace the <script> code with {% include "script.html" %} to include the scripts
  • Replace the lorem ipsum text with {{ content | safe }}. This content variable will be replaced with content from pages that use this template, like the home page.

This is how the files should look.

The contents of default.njk should look like this.

<!doctype html>
<html class="no-js" lang="">

{% include "head.html" %}

<body>
  {% include "header.html" %}
  <div class="content-wrapper">
    {% include "nav.html" %}
    <main>
        {{ content | safe }}
    </main>
  </div>
  {% include "footer.html" %}
  {% include "scripts.html" %}
</body>

</html>

Replace the contents of index.html with the following.

---
layout: default.njk
---
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Facilis, voluptates distinctio fuga culpa praesentium quis repellat, mollitia vel repudiandae suscipit officiis tempora atque ratione quidem quo facere reiciendis, iste sit!</p>

The first section between the triple dashes is the front matter. Here, we tell 11ty to use the default.njk layout for this page. 

The content after the front matter will go where the content variable
{{ content | safe }} is in the layout.

If we look at the home page in a browser, we’ll see that it looks exactly the same as before, except we’ve templatized the home page using a layout and include files. 

Create another page

Let’s create an About page. We have 2 ways to do this:

Method 1: 

Create a file at /about.html.

11ty will convert this to a page at /about/index.html.

Note that any file that is named other than index.html will have a folder created with the same name as the file, e.g. abc.html becomes /abc/index.html.

Method 2: 

Create a file at /about/index.html.

The content of each file can be

---
layout: default.njk
Title: About
---
<h1>{{ title }} </h1>
<p>This is the about page.</p>

In this case, we added a front matter variable called title and output it using curly bracket notation. 11ty is processing these files as Liquid files using the Liquid templating engine.

If we go to http://localhost:8080/about/, we see:

You can also rename about.html to about.njk to use the Nunjucks templating engine. 11ty will process the file according, as you can see in the console.

11ty supports many templating languages including HTML, Markdown, JavaScript, Nunjucks, Liquid, Handlebars, Moustache, EJS (Embedded JavaScript), HAML, Pug, and custom.

Note: You can only choose one method above. If you do both, you’ll get a conflict error, understandably.

Add files for git to ignore

There are some files/folders we don’t want git to track. The HTML5 boilerplate already includes a .gitignore file. Edit it and add “_site” to the list like this. We don’t want to commit the built files to git. Files will be built during deployment.

# Include your project-specific ignores in this file
# Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
# Useful .gitignore templates: https://github.com/github/gitignore
node_modules
dist
.cache
_site

Add the local repo to GitHub

We’ll follow these instructions on how to add our local repo to GitHub.

Create a new repository on GitHub.com. To avoid errors, do not initialize the new repository with README, license, or gitignore files. You can add these files after your project has been pushed to GitHub.

Initialize the local directory as a git repo. Run git init -b main in our project folder. This will create a hidden .git folder.

Add the files in your new local repository. This stages them for the first commit.

git add .

Commit the files that you’ve staged in your local repository.

git commit -m "First commit"

At the top of your repository on GitHub.com’s Quick Setup page, click to copy the remote repository URL.

In the Command prompt, add the URL for the remote repository where your local repository will be pushed.

$ git remote add origin  <REMOTE_URL> 
# Sets the new remote
$ git remote -v
# Verifies the new remote URL

Push the changes in your local repository to GitHub.com.

git push origin main

Go to your repo on GitHub and see your files there.

https://github.com/javanigus/eleventy-basic-site

Host site on Netlify

Now, let’s host our site for free on Netlify. Create a Netlify account and add a new site by importing an existing project.

Connect Netlify to GitHub and choose a repo. If you don’t see your repo, configure Netlify to be able to access some or all repos in your GitHub account.

Netlify will analyze your site and suggest default build settings. As you can see below, Netlify correctly detected that my site was built using 11ty so it populated the “Build command” and “Publish directory” using the default Eleventy values.

Click “Deploy site’ and wait a minute or two for your site to be deployed. Netlify will clone the repo, install any dependencies as listed in the package-lock.json file, run the Eleventy build command, and host the built files. You will then see how long it took to build the entire site and be provided a free Netlify URL where you can view the live site. In this case, the entire build took 37s.

The free Netlify URL generated is https://enchanting-malabi-4c2a4e.netlify.app/

Going to that URL, we see the same site as we saw on our local dev machine.