Mark Thomas Miller's logo

How to set up a blog with Eleventy

June 6, 2020

Eleventy is my favorite static site generator, and it's the tool I use to build this blog. In my opinion, it strikes the perfect balance between minimal and flexible, enabling you to create a blog that works like you think. This is the post I wish I had when I was learning how to use it.

Prerequisites

This tutorial is aimed at people who are comfortable using a text editor and command line. Before we get started, you'll want to make sure you've done a few things:

  • Install npm. If you don't have it, you can get it by installing Node.
  • Sign up for a GitHub account. GitHub will be part of the build process for publishing your site.
  • We'll also be using Git during this build process. If you're not comfortable using it via the command line, you can use a tool like GitHub Desktop.

Creating your Eleventy blog

Your Eleventy blog will live in a folder on your computer. Create a folder for it (you could name it blog or www or my-blog-name or whatever you'd like) and open it in your text editor.

Point your command line to this directory. (If you don't know how to use the command line, I recommend using VS Code as your text editor and following the side tutorial below.)

Using VS Code's terminal

Open your blog's folder in VS Code, then choose any of the following options to open the integrated terminal.

  • Go to Terminal > New Terminal in the Mac menu bar.
  • Press Control + ` (the key below Esc).
  • Press + Shift + P and type "Toggle Integrated Terminal", then hit Enter.
  1. Run the following command:
    npm init -y
    
  2. And then this one:
    npm install --save-dev @11ty/eleventy
    
    This installs Eleventy to your computer. It might take a few minutes to complete.

Create a basic layout

In Eleventy, every one of your pages can have a layout. Layouts are made up of the elements that surround your main content, such as the navigation bar at the top of the page and the footer at the bottom. You can make as many layouts as you want, and design them however you'd like.

Single-column layout on left, layout with sidebar on right

The very first layout you should make is a base HTML layout — that is, a layout to add <head> and <body> tags to your site. To do this, create a folder called _includes in your site's root directory. (When I say root directory, I mean the top level of your site – the same level as package.json and node_modules.) Inside it, create a file named base.liquid. In this file, paste in the following HTML, which will be inserted into every page of our site:

<!DOCTYPE html>
<html lang="en">

  <head>
    <title>Your page title</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="/styles.css" rel="stylesheet">
  </head>

  <body>
    {{ content }}
  </body>

</html>

Now, your folder structure should look something like this:

yoursite/
  _includes/
    base.liquid     # contains the HTML from above
  node_modules/     # ignore this
  package-lock.json # ignore this
  package.json      # ignore this

The _includes folder is special: Eleventy looks there to literally include parts of your site, such as your page layouts. We'll use _includes more in a few minutes.

Create your homepage

Alright, now we're getting somewhere! Back in your site's root directory (next to node_modules, package.json, etc.), create a file called index.md. (Index is a popular word in web development. When placed in a root directory of a website, it is understood to be a site's homepage.) Inside it, paste this text:

---
layout: base.liquid
---

# Welcome to my site!

Not to brag, but it's pretty great.

Start your server

It's time to preview your work so far. To spin up a local server, run this in your terminal:

npx @11ty/eleventy --serve

This will return an output like this:

[Browsersync] Access URLs:
 --------------------------------------
       Local: http://localhost:8080
    External: 🐈
 --------------------------------------
          UI: http://localhost:3001
 UI External: http://localhost:3001
 --------------------------------------
[Browsersync] Serving files from: _site

You've just spun up a local server for your Eleventy blog! You can access it at the Local URL given in the terminal, which will usually be localhost:8080. To stop the server at any time, you can click into the terminal and press Control + C.

Now, what's with that 🐈 emoji? In your terminal, that'll be a URL. That's actually a preview that can be accessed from your local network. In other words, you can type it into your phone to view a mobile preview of your blog while you're developing it. (If you can't access your site at this URL, your firewall settings might be blocking the connection. Don't worry if you can't access it; it's more of a cool feature. It's not required to develop your blog.)

Live preview

VS Code on left, Eleventy on right

Arrange your screen so VS Code is on the left and your browser is on the right. Edit your homepage to say something different, and then save your file with + S. You'll see that the page refreshes to show you your changes automatically. This is called "hot reloading", and it's really nice to have when you're working on a post!

If you're able to visit your blog from the 🐈 URL above, open your homepage on your phone and then edit it in VS Code. What happens on your phone when you save the file?

Add your first post

Things are starting to take shape! You have a basic layout and some HTML. Now, it's time to write your first post.

Create a posts folder in the top level of your site. Inside it, create a file called kitten.md. Your folder structure should now look like this:

yoursite/
  _includes/
    base.liquid
  node_modules/
  posts/            # new!
    kitten.md       # new!
  package.json
  package-lock.json
  index.md

Inside your new kitten.md file, let's write our first post:

---
title: This is my first post
date: 2020-01-30
---

Meow in empty room. Stare at invisible bug on ceiling. Stand
in doorway, unwilling to choose whether to go in or out.
Stretch and yawn. Go back to sleep.

Ooo, feather! Moving feather! Run at 3 AM. Run at 6 AM.
Sleep during day. Mesmerizing birbs. Kick up litter.
Show belly.

We're not done yet — we still need to set the layout. But first, if you'll notice, we give the post a title inside the frontmatter. You could make up some of your own properties too:

Custom frontmatter

You aren't limited to only having a title. You can add whatever you'd like to your frontmatter:

---
title: This is my first post
date: 2020-01-30
mySpecialNote: This is a note!
showCatPicture: true
---

Meow in empty room...

Then, inside your layout files (like base.liquid), you can access them like this:

<h1>{{ title }}</h1>

<div>{{ mySpecialNote }}</div>

{% if showCatPicture %}
  <img src="cat.png" alt="A cat">
{% endif %}

In practice, I've seen these used for toggling different calls to action, setting themes, showing or hiding social sharing links, setting descriptions, working with sitemaps, and so on.

Remember what I said about adding a layout earlier? You could technically add it to your frontmatter like this:

---
title: This is my first post
date: 2020-01-30
layout: base.liquid
---

Meow in empty room...

But it would be annoying to remember to type that on every post. This is a great time to introduce another feature of Eleventy: folder-specific defaults. I think of it like this:

Any time a file is in [folder x], give it this default frontmatter.

Inside the posts folder, create another file called posts.json. Inside of this file, let's assign a default layout, permalink structure, and tags:

{
  "layout": "base.liquid",
  "permalink": "{{ page.fileSlug }}/",
  "tags": ["posts"],
}

This sets the defaults of every file in the posts folder. This helps us keep our frontmatter small and easy to work with. If you're curious, here's what each of the items is doing:

  • layout is setting our layout to our HTML base from earlier. If you make another layout, such as post.liquid, you could change it to that.
  • permalink is making our post URLs simpler and cleaner. Before adding this defaults file, a post inside posts/kitten.md would make it accessible at example.com/posts/kitten/. The permalink code above removes posts from the URL path, so you get a much cleaner URL like example.com/kitten/.
  • tags marks every file in this folder as a post. This'll come in handy for generating our post feed later.

Remember that these are defaults, which means that you can override them anytime from the file itself. For example, if you wanted to give kitten.md a different permalink, you could set it in its frontmatter.

Now, navigate to localhost:8080/kitten/, where you can see your post in its full glory.

Add some surrounding content

We've done a lot of work, but our site is still pretty barren. Let's start adding some more elements to the page. We'll start with a navigation menu.

Let's go back to the _includes folder, which if you'll remember, lets us include things on our site. Create a new file called nav.liquid, and paste in the following content.

<nav>
  <a href="/">Home</a>
  <a href="/kitten/">Kitten</a>
</nav>

Now, re-open base.liquid. Above {{ content }}, add this to include nav.liquid at the top of every page:

{% include nav.liquid %}

Try following the same process to add a footer to your site, and then continue onto the next section.

Add your post feed

Eleventy also gives us access to collections, which we can loop through to generate a post feed. Remember our posts.json defaults file from earlier? Inside it, we assigned a posts tag to every post. We can rely on this to generate a post feed.

Let's add some Liquid to our homepage, index.md:

{% for post in collections.posts reversed %}
  <a href="{{ post.url }}">
    <h2>{{ post.data.title }}</h2>
    <time>{{ post.data.date | date: "%B %d, %Y" }}</time>
  </a>
{% endfor %}

Inside of this loop, we can access the post's frontmatter inside of a data object. For instance, to get our post's title, we can say post.data.title. To get the date, we can say post.data.date. We also apply a filter to the date to make it more readable.

Now, we have a list of every post on our site!

Add styling

Our site is getting there, but it could use a fresh coat of paint. Let's start adding some CSS. I recommend creating a new folder inside _includes called styles. Inside this new styles folder, you can create a few CSS files.

Create variables.css:

:root {
  --font-size: 18px;
  --line-height: 1.5;
  --heading-font: 'Avenir', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  --body-font: 'Iowan Old Style', Garamond, Georgia, serif;

  --heading-color: #000;
  --body-color: #3d3d3d;
}

Create reset.css:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizelegibility;
}

html {
  font-family: var(--body-font);
  font-size: var(--font-size);
  line-height: var(--line-height);
  color: var(--body-color);
}

img {
  max-width: 100%;
  display: block;
}

Create typography.css:

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--heading-font);
  color: var(--heading-color);
  font-weight: 700;
}

h1 { font-size: 2rem; }
h2 { font-size: 1.75rem; }
h3 { font-size: 1.5rem; }
h4 { font-size: 1.25rem; }
h5 { font-size: 1rem; }
h6 { font-size: 0.8rem; }

p, li {
  margin: 1rem 0;
}

To add these styles to your Eleventy site, you'll want to create a styles.liquid file in your root directory and include your CSS:

---
permalink: styles.css
layout: null
---

{% include styles/variables.css %}
{% include styles/reset.css %}
{% include styles/typography.css %}

Add shortcodes

Note: This feature is a bit more advanced. It's not required, so if you don't want to use it, you can skip over this section.

One of my favorite Eleventy features is the ability to add shortcodes. They're a slightly more advanced feature, but they're very powerful! Shortcodes let you create custom, reusable HTML elements that you can embed inside your pages and posts with syntax like:

{% example "..." %}

Here's how you'd create one that adds an <a> tag (a hyperlink) with a class called related that you can style with CSS. First, create a file called .eleventy.js in the root directory of your site. (The name of this file starts with a period.) Inside this file, add this:

module.exports = function (eleventy) {

  // Add related posts
  eleventy.addLiquidShortcode("related", (title, url) => `
    <a href="${url}" class="related">${title}</a>
  `);

};

This will let you use a new shortcode called related in your posts and templates. You'd add it to a post like this:

{% related 'Your post title' '/path-to-your-post' %}

...and it would output:

<a href="/path-to-your-post" class="related">Your post title</a>

Publish your site

Eleventy-generated websites are static, which makes them much easier and cheaper to host than WordPress, Squarespace, Rails, etc. I recommend using a service like Netlify or Vercel to host your site for free. (Hosting a static blog costs so little that most companies write it off as a marketing expense.) I've found Netlify to be a bit easier to use for complete beginners, but Vercel has a beautiful design and slightly faster speeds. You really can't go wrong with either.

Before you publish your site for the first time, you'll want to do a little bit of prep work. First, create a file called .gitignore in your site's root directory. Add this to the file:

node_modules
.DS_Store
_site

If your server is still running, press Control + C to cancel it. Then, type git init and press Enter. If your terminal displays a message that the git couldn't be found, you'll need to install Git:

How to install Git

If your terminal displays a message that Git isn't a command, run:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)

Then, run:

brew install git

After that, you can run the git init command from above. This will initialize a Git repository that you can push to GitHub to host your site.

Once you've initialized a Git repository, you can add this repository to GitHub. I recommend using GitHub Desktop unless you're comfortable using Git via the command line. Use Add an existing repository to add your site as a repo, select all the files, and commit to master. Then push.

Once your site has been added to your remote repository, you can create an account on Netlify or Vercel. Connect their service to the repo for your site; it should automatically default to Eleventy's build commands. Once your site builds, it'll give you a randomly-generated URL. You can defer to each company's docs for adding a custom domain to your site:

Updating your site

After you make changes to your site, push them to master via GitHub Desktop (or the command line). This will automatically kick off a build on Netlify or Vercel, which usually takes ~30-60 seconds.

Next steps

Now that you've set up a basic site, you can move onto styling and assembling its basic components. Here are a few ideas:

  • You could also create an images folder in your root directory where you can drop in the pictures for your posts. Then, you can reference them in Markdown like }![Alt text](/images/example.png).

  • Eleventy's dates might not be accurate, but I have an article on how to fix it.

  • You could create a pages folder in your root directory where you can add pages — for instance, about.md. You can create a folder-specific default file (pages.json) to provide some defaults for these pages.

  • You could add your own sitemap, which provides SEO benefits. I have an article on how to do this here.

  • You could also improve your file organization by creating a folder inside _includes called layouts, and making layouts that inherit from each other. For instance, you could create a post.liquid layout that inherits from base.liquid:

    ---
    layout: layouts/base.liquid
    ---
    
    <article>
    
      <h1>{{ title }}</h1>
    
      {{ content }}
    
    </article>
    

These are just a few of the things you can do. Eleventy is flexible, so you can arrange your blog to fit your work style. Take your time in planning it out, and have fun with it!