How I teach Eleventy from scratch
Eleventy is my favourite static site generator and I’ve been using it for ~6 years now to build almost all of my sites, including this one. I’ve also written a few things about it and spoken in THE Eleventy Meetup about it.
That has led to a few people asking me for help to get started in understanding how it works and how they can build their own site. Instead of starting with a starter kit, I’ve decided to start from scratch with them to help understand the fundamentals of how it works so they can build on top of that knowledge even if they then choose to kickstart their site with a starter kit.
What I really like about Eleventy is that you can build things so incrementally. You can start with a single file and single command and every step of the way, your outcome is a working website. Let’s take a look at how!
The audience to which I’ve been going this workshop through with have been software developers who are already familiar with how to run command line tools, install packages and so on, so it’s not an absolute beginner class.
In this workshop, I won’t teach you how to build a website. The HTML, CSS and Javascript bits nor the actual content. If you’re new to all that, I recommend starting from MDN’s Learn web development guide. This workshop focuses on Eleventy parts of combining all those things in an effective way.
Prerequisites
You need to have Node.js, npm and npx installed to be able to follow along. In addition, a code editor is needed and familiarity with using terminal helps a lot.
Once everything is installed, create a new folder for your project.
Step 1: index.md
In a nutshell, static site generators are tools that turn markdown files into html files. They do a lot of other things as well but that’s a good baseline to start with.
So create a file called
index.md
and add to it:
Hello world!
Then run npx @11ty/eleventy --serve
in
terminal. You should see something along the lines of:
[11ty] Writing ./_site/index.html from ./index.md (liquid)
[11ty] Wrote 1 file in 0.36 seconds (v3.0.0)
[11ty] Watching…
[11ty] Server at http://localhost:8081/
(if npx asks if you want to install
@11ty/eleventy
, say yes)
Then open the server at the given localhost URL in a browser and you should see your first Eleventy website: a white page with black text that says “Hello world!”
Welcome to the world of building static sites with Eleventy.
While this website isn’t very useful yet, I like how it’s all you need to get started with Eleventy. You don’t need to generate 15 folders and 100 files to get a text to show up on a website. Thanks to npx, you don’t even a package.json file to manage the project yet.
💡 In this step, you learned how to create a markdown file and run Eleventy to turn it into a web page.
Step 2: Let’s write a blog post
A great reason to build your own static website is to start blogging! Blogging is fun and beneficial to you in many ways and Eleventy is a good engine to build one with.
To start a blog, let’s create a new folder called
blog/
and create our first blog post
blog/my-first-post.md
:
# My first post
I decided to start blogging and build my blog with [Eleventy](https://11ty.dev)
Then run
npx @11ty/eleventy --serve
again it’s
not already running and open the browser.
We don’t yet have any links to the post itself from the index page but you can
manually navigate to it by adding
/blog/my-first-post/
to the end of the
URL.
The blog post written in Markdown was processed into HTML with headings and links.
Blog post metadata
You can add metadata in YAML format into front matter of your blog posts. Let’s add some now to our post.
---
title: My first post
date: 2024-11-05
tags: blog
---
# My first post
I decided to start blogging and build my blog with [Eleventy](https://11ty.dev)
Front matter is added by adding a block that starts with three dashes (—-
) and ends with three dashes (—-
) to the
very beginning of your file. These will not be visible in the post itself but
can be used later on with templating.
💡 In this step, you learned how to create a blog post, add a bit of metadata and navigate to it on your site.
Step 3: Blog index
To make it easier for our readers to find our blog posts, let’s create a
listing of our blog posts in our index page. To do that, we need to switch our
index.md
to another templating language that supports a bit more features. Eleventy
supports quite a few, I like using
Nunjucks
because it’s similar to Jinja that I’m used to from Django world.
-
Rename
index.md
toindex.njk
Change the contents of index.njk
to:
<h1>My blog</h1>
<ul>
{% for post in collections.blog %}
<li><a href="{{post.url}}">{{post.data.title}}</a></li>
{% endfor %}
</ul>
We’re doing a couple of new things here.
First, we loop over a
collections.blog
which is a collection of all files that have a tag of “blog”. In the previous
step, we added tags: blog
to our blog
post which tells Eleventy to add it to this particular collection.
Next, inside the loop, we access
post.url
which is internal metadata
created by Eleventy as it processes through all the files. Anything inside our
custom front matter, we access through
post.data
object. In this case, we want the link text to be the title of the post.
Now run the server and check the results. We have our blog post in a list!
It’s a bit lonely though so let’s give it a friend.
Create blog/my-second-post.md
with
---
title: My second post
date: 2024-11-06
tags: blog
---
# Second post in two days
I'm having a blogging streak! Let's go.
With this file saved, our listing now shows both blog posts. We’re now officially bloggers!
💡 In this step, you learned about using Nunjuck templating language to create
a list of posts and how to access data from individual posts. You also learned
that by specifying tags
, you can add
files to Eleventy collections.
Step 4: Layouts and includes
So far, our styling and layout has been very barebones. One key feature in modern static site generators is the ability to include other bits of code in different places. This separates them from writing all your HTML code manually by hand. Write it once and use it in many places!
Let’s make a base layout for our website.
-
Create a new folder
_includes/
and inside it,_includes/layouts/
. -
Create a new file
_includes/layouts/base.njk
Base template
Inside base.njk
, write
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My blog</title>
</head>
<body>
<main>
{{ content | safe }}
</main>
</body>
</html>
This is our starting point for creating a layout. You can add all your
<head>
tags inside it and they
will then be spread out to everywhere where we use this layout.
The
{{ content | safe }}
tells our template to put all of the content of a file that’s using this
template here. The safe
filter (we’ll
talk about filters soon, I promise) tells it to render any HTML we pass it.
Let’s use it
Tell our index.njk
to use it. At the
start of the file, add
---
layout: layouts/base.njk
---
Your page doesn’t change much but if you look at the source for the page in your browser, you can see it was wrapped into the scaffolding we wrote into the template.
Template for a blog post
Next, let’s create another template for our blog post.
Create _includes/layouts/post.njk
and
write:
---
layout: layouts/base.njk
---
<article>
<h1>{{title}}</h1>
<time>{{date}}</time>
{{content|safe}}
</article>
Notice how our layout is using another layout! We can chain these templates into each other, making it easier to compose smaller pieces and combine them as we need.
Here, we can access our front matter data directly so we can use
{{ title }}
and
{{ date }}
like
that.
Next, we need to tell our blog posts to use this layout.
In both of the files in blog/
, add the
following to the front matter:
layout: layouts/post.njk
We now have our title twice there. Let’s remove the title from the actual post
contents in our markdown files by removing the
# My first post
and
# My second post
respectively in the two
blog post files.
Much better!
The date looks a bit too specific but let’s worry about that a bit later.
💡 In this step, you learned how to create HTML layout templates and how to apply them to other files.
Step 5: Eleventy configuration
We’ve gotten quite far without defining anything for Eleventy ourselves. Now it’s time to take a look at how we can configure Eleventy to help make our work easier.
Let’s start by creating
eleventy.config.mjs
into the root of the
project. In Eleventy, we define our
configuration
using Javascript.
CSS and image assets
Until now, every file we’ve created has been processed by Eleventy in some way
on its way to the _site
folder where our
final static site lives in. Next, we want to add images and CSS styles that we
want copied as-is to _site
.
Create a new folder assets/
in the root.
Inside, create subfolders css/
and
images/
.
A side tip: if you want to create multiple
folders like this in command line, you can do
mkdir -p assets/{css,js}
. It’s called
brace expansion
and it’s very handy!
Next, let’s tell Eleventy that we want anything in
assets/
to be copied as-is to
_site/assets
every time a new build is
created. In eleventy.config.mjs
add:
export default async function (eleventyConfig) {
eleventyConfig.addPassthroughCopy("assets");
}
A few things of note here. The first line is how we tell Eleventy this is the function we want to use for our configuration. It’s in ESM format that is supported in Eleventy 3.0.0 onwards.
It provides us eleventyConfig
object
that is our API to all things we want to configure. We use
addPassthroughCopy
to define which folders and files we want to be passed as-is without trying to
process them further. In this case, everything inside
assets/
folder.
Let’s fix that date format with a filter
In our previous step, when we passed a date to Nunjucks templating, we got a very specific format out. Sometimes it can be useful but often we want something more user friendly. Let’s say we want to show our blog post dates as “[month written out] [date], [year]”.
We can create a custom filter to do it. In Eleventy, filters are functions we can run from our templates and they will transform the input into an output format we like.
First, outside the function we already have, let’s create a new function
called blogDate
:
function blogDate(input) {
return `${new Date(input).toLocaleString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}`;
}
We then register this as a filter inside the configuration function with:
eleventyConfig.addFilter('blogDate', blogDate)
The first argument is the name of the filter, as used in templates and the second is the function to be called.
To use this filter in our templates, we change in
_includes/layouts/post.njk
:
// from
<time>{{ date }}</time>
// to
<time>{{ date | blogDate }}</time>
Note that when you make changes to the Eleventy configuration files, you
sometimes may need to restart the server (with
npx @11ty/eleventy --serve
). So if your
filters are not being applied, try restarting it and see if that does the job.
There are a few
built-in filters
in Eleventy and you can make as many as you wish yourself. A very handy
built-in filter is log
which prints the
contents into the terminal, helping debug when things don’t work.
💡 In this step, you learned how to create an Eleventy configuration file, how to pass folders and files as-is to the output folder and how to create and apply custom filters.
Step 6: Plugins
The final thing I want you to know about is the plugin ecosystem. You can use plugins written by different people to enhance your Eleventy.
One that I want to introduce early on is the official RSS plugin so your blog will be set up for people to follow via RSS feed from the beginning.
To be able to install plugins, we need to set this site up with npm.
Run
npm init -y
in the root of the project. This will initialise the project and create a
package.json
file.
We can then install Eleventy and the plugin into this project with
npm install --save-dev @11ty/eleventy @11ty/eleventy-plugin-rss
Inside eleventy.config.mjs
, at the top,
import the plugin:
import { feedPlugin } from "@11ty/eleventy-plugin-rss";
and inside the configuration function, activate it:
eleventyConfig.addPlugin(feedPlugin, {
type: "rss",
outputPath: "/feed.xml",
collection: {
name: "blog",
limit: 10, // 0 means no limit
},
metadata: {
language: "en",
title: "My very cool blog",
subtitle: "I'm writing about stuff",
base: "https://example.com/",
author: {
name: "Juhis",
email: "", // Optional
}
}
});
When you run Eleventy, it will create a new file,
_site/feed.xml
with your blog as its
contents. You can tweak the configuration or create the RSS template manually
by following
the documentation.
💡 In this step, you learned how to install and activate a plugin and made your blog better for your readers by providing a RSS feed.
Conclusion
There’s a lot that goes into building a website. This short workshop is intended to give a basic fundamental understanding of how Eleventy works by building a site from scratch. If you don’t want to build everything from scratch for your real site, you can take a look at the starter projects built by the community.
You should also join our wonderful community. We have an active Discord server, a lovely meetup and a great newsletter that gathers resources so you can keep on learning and being inspired by other people.