Skip to content Go to Sitemap
You're 0% done reading!

11ty (eleventy) Tutorial

Eleventy is a static site generator. You can create "templates" (either .html or .njk files) and re-use them. This way you don't have to edit your header in every single page file, etc. Then you execute a command, and Eleventy will generate all your page files for you. These files you can upload to your website as you normally would.

If you want to look at an example project that uses Eleventy, check out my website's repository!

If you have questions or feedback regarding the tutorial, please use the comment section at the end of the page! Don't be shy; I know it's frustrating when you're stuck, and I'd love to help.


Requirements

You need to have Node.js ("node") (version 14 or higher) and npm installed on your computer. Your project needs a package.json. You need to be able to use a command line.

Read my npm tutorial to find out how to do all of these things.

I also highly recommend using a program for coding that has a built-in terminal, such as VSCode.

In your terminal, check your installed version of node and npm:

  • node -v should print 20.17.0 or higher

  • npm -v should print 10.8.2 or higher

If your version isn't up-to-date enough you simply need install the latest Node.js on your device.

Installation

In your project, execute the following command to install eleventy as a dev dependency for your project:

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

You should now see "@11ty/eleventy" in the list of your dependencies in your package.json file.


If you're using VSCode, I highly recommend installing the Extension "Better Nunjucks" to get syntax highlighting for Nunjucks code.

Configuration

In the root of your project, create a file called .eleventy.js (don't forget the period at the start of the filename!).

This file is where we will put all our configuration (settings) for Eleventy. Basically, here we tell it how we want it to work.

The most minimal version of this file looks like this:

module.exports = function (eleventyConfig) {

  // This will stop the default behaviour of foo.html being turned into foo/index.html
  eleventyConfig.addGlobalData("permalink", "{{ page.filePathStem }}.html");

  return {
    dir: {
      input: "content",
      output: "public",
    },
  };
};

input defines the name of the folder in which your templates and pages will go. This is the folder in which you will make your changes. In this example I called it "content", but you can choose any name you like (one common name would be "src" - short for "source").

output defines the name of the folder into which the finished html files will be generated. In this example I called it "public", but you can choose any name you like (common names are "public" and "dist" - short for "distribution"). Eleventy doesn't delete files, so this folder can also contain other things, such as your images or other assets. Essentially, the contents of the "public" folder will be what you upload to your website. Everything else (such as the input folder) is only necessary for development, and therefore does not need to be uploaded.

Here is an example of a larger configuration file, with explanations:

module.exports = function (eleventyConfig) {
  // This makes the eleventy command quieter (with less detail)
  eleventyConfig.setQuietMode(true);

  // This will stop the default behaviour of foo.html being turned into foo/index.html
  eleventyConfig.addGlobalData("permalink", "{{ page.filePathStem }}.html");

  // This will make Eleventy not use your .gitignore file to ignore files
  eleventyConfig.setUseGitIgnore(false);

  // This will copy this folder to the output without modifying it at all
  eleventyConfig.addPassthroughCopy("content/assets");

  // This defines which files will be copied
  eleventyConfig.setTemplateFormats(["html", "njk", "txt", "js", "css", "xml", "json"]);

  // This defines the input and output directories
  return {
    dir: {
      input: "content",
      output: "public",
    },
  };
};

Nunjucks

In this tutorial I will show you how to use Nunjucks to create templates. (There are also other templating languages you can use with Eleventy.) Eleventy will turn our Nunjucks templates into normal HTML files. You don't need any additional things to write Nunjucks code, but if you're using VSCode i recommend installing the Extension "Better Nunjucks" to get syntax highlighting for Nunjucks code.

You can use Nunjucks in your .html files (located in your input folder) without a problem. Alternatively, you can also create a Nunjucks file by giving the file the extension .njk. This is necessary for some files (see later). .njk files can contain normal HTML code, don't worry.

For a full documentation on what you can do with Nunjucks and how, go here. Here are some of the most important things:

Setting a variable:
    {% set myText = "Hello World!" %}
    {% set showHeader = true %}

Outputting a variable:
    {{ myText }}

Outputting a variable (without escaping HTML):
    {{ myHTML | safe }}

If:
    {% if showHeader %}
      <header>...</header>
    {% endif %}

If/Else:
    {% if hungry %}
      I am hungry
    {% elseif tired %}
      I am tired
    {% else %}
      I am good!
    {% endif %}

For Loop:
    {% for item in items %}
      <li>{{ item.title }}</li>
    {% else %}
      <li>This would display if the 'item' collection were empty</li>
    {% endfor %}

Note: Logical operators like &&, || and ! as you know them from JavaScript do not exist in Nunjucks, you need to use the words "and", "or" and "not", e.g.:

{% if myVariable1 and myVariable2 %}...{% endif %}
{% if myVariable1 or myVariable2 %}...{% endif %}
{% if not myVariable %}...{% endif %}

Comparisions work the same as in JavaScript (e.g. ==, !=, <= ...)

Using Data (Optional)

The variables you use can come from several different data sources, such as:

  • that very file ({% set myText = "Hello World!" %})
  • the "front matter" of that file (see later) (myText = Hello World)
  • a data file in the _data subfolder of your input folder.

I will now describe data files in detail.

In your input folder (in my example that is the "content" folder), create a subfolder with the name _data. In there, you can create JSON files (.json) with data. You can then reference that data by using the file name as a variable name in Nunjucks.

If you don't know JSON syntax: don't worry. It's pretty much self-explanatory if you know JavaScript. As usual, [] are arrays and {} are objects.

Example: changelog.json

[
  { "date": "2024-12-07", "text": "Created page" },
  { "date": "2024-12-08", "text": "Bugfixes" },
]

Usage in Nunjucks:

<ul>
    {% for item in changelog %}
      <li>{{ item.date }}: {{ item.text }}</li>
    {% endfor %}
</ul>

(This would output a list of all objects in the changelog.json file.)

There are also other ways to get data. If you're interested read the documentation.

Partials (Optional)

You can create .njk files and include them in your pages like so:

<header>
    {% include 'headerImage.njk' %}
    {% include 'menu.njk' %}
</header>

These files must be located in a _includes folder in your input folder. (In my example this would be content/_includes.)

Templates

Step 1: The template

Template files must be located in a _includes folder in your input folder. (In my example this would be content/_includes.)

A template file needs to have the file extension .njk, but besides that it can look like a totally normal HTML file. You can (and should) use Nunjucks code to output things that vary from page to page, such as page title, etc. (Like so: <title>{{ title }}</title>) We will define the values of those variables in the page files in the next step.

Step 2: The page

Whether your page file is an .html or a .njk file doesn't matter - You can use templates in the same way.

All we need to do is the so-called "front matter". It's a section at the top of the file that looks like this:

---
layout: myTemplate.njk
title: About Me
showSelfie: true
---

This example defines three variables and gives them values. For text variables (strings) the quotation marks are optional. This would work just as well: title: "About Me".

You can choose the names of the variables, except for layout. layout always defines the filename of the layout you want to use.

The rest of the file is normal HTML (with some Nunjucks code if you want).

Wherever you want the content of the page to be, type {{ content | safe }}

Tip: You can even create other types of files, such as .xml files, with Nunjucks. Simply create the file with the extension .njk but add the permalink variable to the front matter, e.g.: permalink: "rss.xml", which would turn that file into rss.xml. (Here is a complete example on how to generate an RSS file with Eleventy.)

Example

content/_includes/myTemplate.njk:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>{{ title }}</title>
    <script src="{{ nesting }}main.js"></script>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
    <meta content="utf-8" http-equiv="encoding" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="{{ nesting }}favicon.ico" rel="icon" type="image/x-icon" />
  </head>
  <body class="{{ bodyClass }}">
    <header>Header</header>
    <main>
      {{ content | safe }}
    </main>
    <footer>Footer</footer>
  </body>
</html>

content/about-me.html (or .njk):

---
layout: myTemplate.njk
title: About Me
bodyClass: about-me-page
nesting: "../"
---

<h1>About Me</h1>
<p>Here is the main content of your file...</p>

The generated file would be public/about-me.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>About Me</title>
    <script src="../main.js"></script>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
    <meta content="utf-8" http-equiv="encoding" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="../favicon.ico" rel="icon" type="image/x-icon" />
  </head>
  <body class="about-me-page">
    <header>Header</header>
    <main>
      <h1>About Me</h1>
      <p>Here is the main content of your file...</p>
    </main>
    <footer>Footer</footer>
  </body>
</html>

Pay close attention to the nesting variable I defined. This is a good way of making sure that the relative paths in the template work for all pages, no matter the folder depth of the page.

In a page file in the root folder (such as index.html, your home page) the value would be ./, in a page 1 folder deep it would be ../, in a page 2 folders deep it would be ../../ and so on.

Keep in mind that the paths are relative to your output folder. This means, in the example above, main.js and favicon.ico are both in the output folder: public/main.js and public/favicon.ico.

Extending files

An alternative to using Eleventy templates with front matter (as described in the previous step) is to extend an njk template. This will give you more freedom than the simple templates as described earlier. However, it is often not really necessary. I highly recommend not doing this unless you actually have to.

Show me how

This is only possible in .njk files and you don't need the "front matter" described earlier.

Blocks defined in the extended file can be overwritten by re-defining the blocks in the other file.

Here is an example:

myTemplate.njk (which defines the title block and gives it default content):

...
<header>
    {% block title %}<h1>Hello World!</h1>{% endblock %}
</header>
...

about-me.njk (which extends the template and overwrites the content of the title block):

{% extends 'myTemplate.njk' %}
{% block title %}<h1>About Me</h1>{% endblock %}

would output:

...
<header>
    <h1>About Me</h1>
</header>
...

Running Eleventy

Execute this command in your terminal to run Eleventy: npx @11ty/eleventy

This will generate your .html files based on the files (such as templates) in your input directory. You have to execute this command after every change you make in your input folder. To preview your website, use the files in the output folder or the web server (see below).

You can also make Eleventy 'watch' your files for changes, so that you don't have to manually execute the command after every change:

  • npx @11ty/eleventy --watch watches your files
  • npx @11ty/eleventy --serve watches your files and starts a web server

If you do this, whenever you save a change, Eleventy will generate the html files anew. If you're using the web server (aka viewing your website at localhost, e.g. http://localhost:8080/) the page will even refresh for you automatically.


Defining a custom command (Optional)

In your package.json file, you can define custom commands. Here is an example:

This would make the command npm run build execute npx @11ty/eleventy:

{
  ...
  "scripts": {
    "build": "npx @11ty/eleventy"
  }
}

I highly recommend this because it's less of a handful to type out.

You can even combine the eleventy command with other commands you might be using, such as webpack:

{
  ...
  "scripts": {
    "build": "npx @11ty/eleventy & webpack",
    "watch": "npx @11ty/eleventy --serve & webpack --watch",
  }
}

(These will run both the Eleventy and the Webpack commands at the same time.)

Done!

Your project structure will look a bit like this:

content/			<-- your input folder
    _data/
        myData.json		<-- a data file you can access as a variable
    _includes/
        myTemplate.njk		<-- a template you can re-use
        header.njk		<-- a file you can include in another one
    about/
        about-me.html		<-- this is where you make changes
public/				<-- your output folder
    about/
        about-me.html		<-- generated automatically, don't edit this
.eleventy.js			<-- the configuration file

REMEMBER:

  • Do not edit the generated files (in the output folder), as they will be overwritten the next time you run Eleventy. Only make changes in the input folder.
  • Run npx @11ty/eleventy after every change (or let Eleventy watch your files and do this for you) to see the changes.
  • The contents of the output folder are what you upload to your website.
If you want to look at an example project that uses Eleventy, check out my website's repository!

Here are some links to official documentation & other helpful stuff.

I spend hours of my free time writing these tutorials that I publish for free. If you'd like to say thanks, please share this tutorial with others and/or buy me a coffee!

Comments

Leave your questions, problems, suggestions, requests and thanks here!

To share very long code or error messages with me, please upload it to pastebin (or a similar site) and include the link. This is to make sure you don't hit the max character limit on comments.