Creating A Blog With Eleventy

 |  Eleventy, Static Site Generator

This article covers creating a blog from scratch using the Node.js powered static site generator Eleventy (aka 11ty). Eleventy is a relatively new project that came out in 2018 and is quickly gaining traction. It’s up to an impressive 3,000 stars on GitHub already. Eleventy keeps things simple and as you’ll see, really enables you to quickly create a fully functional site.

The blog we’ll create will contain a homepage with excerpts from our posts that can be clicked on to read the full post. Once we have our blog up and running, adding new posts will be as simple as adding new Markdown files. Eleventy will take care of generating HTML pages seamlessly for us as well as updating the homepage.

The code for this article is available on GitHub:

A demo of the blog is hosted on Netlify:

alt text

Creating the project

Create the folder eleventy-blog for our project and then within this folder, create a new package.json.

mkdir eleventy-blog
cd eleventy-blog
npm init

When the npm init command is run, you will be asked a series of questions which can all be answered with the default values.

With the package.json file in place, we can now install Eleventy locally as a development dependency.

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

Note that the package is within the scope @11ty. Another completely different npm package exists with the name eleventy.

Now let’s create the Markdown file for the about page on our blog.

# About This Blog 
This is my special place on the big World Wide Web

And with these steps alone, we are ready to have Eleventy generate our first HTML page. Run npx eleventy.

npx eleventy
Writing _site/About/index.html from ./
Processed 1 file in 0.26 seconds

You should now have a new folder _site created for you by Eleventy which contains the folder About and inside this folder, the HTML file index.html.

<h1>About This Blog</h1> 
<p>This is my special place on big World Wide Web</p>

When Eleventy finds a template content file like a Markdown file, it generates an index.html under a folder named the same as the original filename but without the file extension. E.g. the file Pages\ will result in index.html being created under the folder _site\Pages\Help.

The default output folder is _site and the default input folder is the root directory. These can be configured via an .eleventy.js config file in the root directory. Later in this article, we’ll create this file but for other reasons. You can read more about configuration in the official documentation.

Serving up the site

Having Eleventy generate the HTML for our blog posts is great but while we’re developing and adding content, we really want to run a web server so we can see our pages in the browser and even better than that, have the browser update whenever we make changes. Well guess what… Eleventy has got this covered!

First we’ll add some script entries to our package.json for the different Eleventy commands we’re going to use.

"scripts": {
  "build": "npx eleventy",
  "serve": "npx eleventy --serve",
  "debug": "DEBUG=* npx eleventy"

Next we’ll create a basic layout file that will contain HTML markup to wrap around the HTML that Eleventy generates for each Markdown file. The layout file will ensure that the final output is valid HTML with <html> and <body> tags. The layout file will also contain standard elements we’d like on every page such as a header and footer.

Create the folder _includes and within this folder create the file base-layout.njk with the following content.

<!DOCTYPE html> 
<html lang="en"> 
  <meta charset="utf-8"> 
  <meta name="viewport" content="width=device-width, initial-scale=1" /> 
  <title>Eleventy Blog</title> 
    Site Header
    <a href="/About">About</a>
    {{ content | safe }}
  <footer>Site Footer</footer> 

Layouts are searched for in the “includes folder”, which is _includes by default. This can be configured to another location if required.

A .njk file is a Nunjucks template that can contain, among other things, loops, conditional logic, special formatting and variable handling. With the line {{ content | safe }}, the template will take the HTML generated from the Markdown file and combine this into the HTML generated for the layout template. The | safe part runs a filter that ensures the HTML from the Markdown is not escaped. You can learn more about Nunjucks on the official site

To use this layout, we need to add some front matter metadata at the top of our file.

layout: base-layout.njk 
# About this blog 
This is my special place on the big World Wide Web

We can now start the local web server with hot-reloading via the following command.

npm run serve

Going to http://localhost:8080/About in a browser (your port may vary) shows our first page in all it’s glory!

If you change the Markdown file or the layout template, Eleventy will generate the new HTML and update the browser almost immediately. Similarly, if you create a new Markdown file, Eleventy will detect this and automatically generate a new HTML file.

Creating the homepage

What we’d like on our homepage is to list the recent blog posts we’ve added to our site. Let’s first create some dummy blog post Markdown files so we have some content to show. In a new folder posts\2019 create the files and with following content.

layout: base-layout.njk 
title: This Is My First Ever Post
date: 2019-05-30
tags: ['post']
This is the first post on my blog.
Eleventy is super fresh init.

layout: base-layout.njk 
title: How To Get Rich Quick
date: 2019-06-01
tags: ['post']
Buy the latest and greatest cryptocurrency that no-one has heard of.
## Legal Stuff
We do not accept any liability for any loss or damage.

Note that each blog post is using the base-layout.njk Nunjucks template and the title for each blog article is in the front matter metadata.

We’ll now create an index.njk Nunjucks template for the homepage of our site. Create index.njk in the root directory with the following content.

layout: base-layout.njk
  size: 10
  reverse: true
  alias: posts
{% for post in posts %}
      <a href="{{ post.url | url }}">{{ }}</a>
    {{ }}
{% endfor %}

Each of our blog articles has the tag of post assigned to it in the front matter metadata. Eleventy provides a collection for each tag that’s been assigned, so we can access all our blog posts via the collection The pagination metadata specifies that the variable posts will be available in the template and will contain the last 10 blog posts. In the template, we’re then iterating through each post and outputting a link to the post and outputting the date.

alt text

Looking at the homepage, the date isn’t formatted in a particularly nice way and should be formatted to be easier to read. Eleventy has no in-built mechanism for formatting dates, so we’ll create our own “filter” to handle this. We’re going to use the npm package “moment” to handle date formatting so let get that installed.

npm install moment --save-dev

We’ll then create the Eleventy configuration file .eleventy.js where we’ll add some date filters.

const moment = require('moment');
module.exports = function (eleventyConfig) {
  eleventyConfig.addFilter('dateIso', date => {
    return moment(date).toISOString();
  eleventyConfig.addFilter('dateReadable', date => {
    return moment(date).format('LL'); // E.g. May 31, 2019

This will make the filters dateIso and dateReadable available for us to use within our Nunjucks templates. We’ll use both of these date formats as we’re going to output a <time> element which uses the ISO format to assist with search engines understanding the date.

Update index.njk to use the new filters.

<time datetime="{{ | dateIso }}">{{ | dateReadable }}</time>

This results in our dates being much more readable.

alt text

Displaying excerpts on the homepage

When people view the homepage of our blog, we’d like to entice them to read a post by displaying the first part of it for them to see. This isn’t something Eleventy has built-in support for but is something we can add in via a “shortcode”. Add the following function at the very bottom of .eleventy.js (after the module.exports function).

function extractExcerpt(article) {
  if (!article.hasOwnProperty('templateContent')) {
    console.warn('Failed to extract excerpt: Document has no property "templateContent".');
    return null;
  let excerpt = null;
  const content = article.templateContent;
  // The start and end separators to try and match to extract the excerpt
  const separatorsList = [
    { start: '<!-- Excerpt Start -->', end: '<!-- Excerpt End -->' },
    { start: '<p>', end: '</p>' }
  separatorsList.some(separators => {
    const startPosition = content.indexOf(separators.start);
    const endPosition = content.lastIndexOf(separators.end);
    if (startPosition !== -1 && endPosition !== -1) {
      excerpt = content.substring(startPosition + separators.start.length, endPosition).trim();
      return true; // Exit out of array loop on first match
  return excerpt;

Then from within the module.exports function, register the excerpt “shortcode” with Eleventy.

eleventyConfig.addShortcode('excerpt', article => extractExcerpt(article));

This “shortcode” will take the HTML output from each article and return just the content between the HTML comments <!-- Excerpt Start --> and <!-- Excerpt End -->. If these comments aren’t found, it will instead return the content between the first <p> and the last </p>.

Update the blog post Markdown files to wrap the <!-- Excerpt Start --> and <!-- Excerpt End --> HTML comments around the content you want to show as the excerpt for each post on the homepage. For example:

<!-- Excerpt Start -->
This is included on the homepage.
<!-- Excerpt End -->
This will not be on the homepage.

Now we just need to use the excerpt “shortcode” in the index.njk homepage template. We’ll also add a “Read more” link at the bottom of each excerpt.

<time datetime="{{ | dateIso }}">{{ | dateReadable }}</time>

{% excerpt post %}

<a href="{{ post.url | url }}"
   aria-label="Read more on {{ }}">Read more</a>

The blog is starting to take shape nicely.

alt text

Blog post nested layout template

If we look at each of our blog posts, they don’t display the title or the date available in their front matter metadata. We can easily solve this by creating a nested layout template for use with blog post Markdown files. Create a new layout template post-layout.njk in the folder _includes with the following content.

layout: base-layout.njk
  <h1>{{ title }}</h1> 
  <time datetime="{{ date | dateIso }}">{{ date | dateReadable }}</time> 
  {{ content | safe }}

This will output the the post title and date for each post. This layout template itself uses base-layout.njk layout, so it’s a nested layout template. To use this layout template, update the front matter metadata in each blog post Markdown file to layout: post-layout.njk.

Congratulations, you’ve got yourself a blog setup where adding new posts and having the homepage update is as simple as creating a new Markdown file.

Show me the code!

The code for this article is available on GitHub:

The latest version of the code in master has a few additional changes to use Google Fonts and use a CSS file to smarten the site up.

A demo of the blog is hosted on Netlify: