Building an Eleventy Starter Template: Project Setup and Configuration

04-06-22 Dustin Whisman

In the first part of our Eleventy Starter Repo Series, we set some goals for your ideal GitHub starter template. Today, you'll learn the basics for setting up and configuring your project, including building your first HTML page.

In the introduction to this series, we set some goals for an ideal starter template using Eleventy. In this entry, we’ll go over how to set up the project structure and configuration. At the end, we’ll build our first HTML page.

If you just want to see the code (or use the starter template), you can find the repo at https://github.com/dustin-jw/eleventy-starter. You can also see a snapshot of what the codebase looks like after following the steps in this article by going to the project-setup-and-configuration branch.

Setting up the Project

Like any project, we need to take care of some basic project setup tasks.

# create a new folder for the project
mkdir eleventy-starter

# change working directory to the new folder
cd eleventy-starter

# initialize a new git repo
git init

# initialize npm with default options
npm init -y

Excellent! Now we should have a folder set up as a git repository with a package.json file. Before we install Eleventy, we’ll need a .gitignore file to keep us from tracking any unwanted files or folders. We’ll start with node_modules, so we can avoid some noise in our git history. Our build process will overwrite dist whenever we deploy, so we’ll also ignore that folder. Even though we don’t have a .env file yet, we’ll add that to the .gitignore so we don’t accidentally commit API keys or other secrets in future projects.

node_modules
dist
.env

This is a good time to include any other optional configuration files for the development experience, so add any .editorconfig or .prettierrc files, etc. that you normally use while developing.

It’s also a good time to add a README.md file, which we’ll be updating throughout this series as we build the starter template. For now, we’ll keep it simple and only include a title and a brief description.

# Eleventy Starter

Use this starter template to create a new Eleventy project with the click of a button!

With all this in place, it’s a good time to make our initial commit.

git add .

git commit -m "chore: initial commit"

Generating Our First Page

Now let’s install Eleventy and set up a couple of npm scripts to run it.

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

In our package.json, we’ll want to add a start script to run Eleventy in development mode and a build script to run our production build. We can also delete the default test script that our npm init created (we’ll get to testing later in this series when we set up our JavaScript build process).

{
  "scripts": {
    "start": "eleventy --serve",
    "build": "eleventy"
  }
}

The eleventy command will build our site by taking our source files, processing them, and putting the resulting HTML files into a folder (currently the _site folder). Adding the --serve command will start a local development server, usually on port 8080, and rebuild the site when files are changed.

With that in mind, our start script is what we’ll be using during development since it will rebuild the site as we make changes, and our build script is what we’ll use when we want to build the site for production.

Let’s go ahead and run npm start. We should see some output that tells us that Eleventy built our README.md file into _site/README/index.html, and we ought to see that file added to our project. If we open http://localhost:8080/README, we should see our README rendered as HTML. If you make some changes to README.md and save, Eleventy will rebuild the page, so you’ll see the updates when you reload the page.

So that’s cool, but it’s not quite what we want. For starters, the HTML that is output is missing some key structural elements, and we don’t want Eleventy to be publishing our README as part of the site. Let’s delete the _site folder and work on defining the project structure to suit our needs.

Defining Our Project Structure

The ideal project structure should be easy to navigate so that newcomers can understand the project without much trouble. Let’s organize our project so that configuration and tooling happen at the root, the code for the site itself goes into src, and the end result gets published to dist.

Within src, we will want a couple of folders to start with:

  • pages: this will be where we put our individual pages
  • partials: this will be where we put layouts and reusable HTML templates

We can configure this with Eleventy by adding a config file named .eleventy.js at the project root.

module.exports = function(eleventyConfig) {
  return {
    // configuration object for directories
    dir: {
      // Eleventy will look here for files to process
      input: 'src/pages',
      // the built files will be placed here
      output: 'dist',
      // tells Eleventy where to look for layouts/partials
      includes: '../partials',
    },
  };
};

Now let’s create an index.md file in src/pages.

# Hello World!

This is the home page!

If we run npm run build, we should see that file get transformed into HTML and dropped into dist/index.html. However, it will still be invalid markup, so let’s fix that.

Creating Our HTML Layout

Our HTML layout is going to be a shell that will surround the content from individual pages. It will be reused by different pages, so our main concern is the document structure. We’ll use Nunjucks as our templating language throughout this project, so let’s create a layout.njk file in the src/partials folder and set up our bare minimum HTML structure.

<!doctype html>

<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- TODO: make these dynamic -->
    <title>Eleventy Starter</title>
    <meta name="description" content="This describes the content of the page.">

    <!-- TODO: add link tags, other meta tags, open graph info, etc. -->
  </head>
  <body>
    <!-- TODO: make the header and footer overridable -->
    <header>
      <p>This is the header.</p>
    </header>
    <main>
      {{ content | safe }}
    </main>
    <footer>
      <p>This is the footer.</p>
    </footer>
  </body>
</html>

Breaking this down, we have some boilerplate that isn’t likely to change, such as the <!doctype html> declaration, html and body tags, and the first two meta tags. Then we have our title and meta description, which we’ll want to make dynamic in the future (stay tuned for Part Two!). And we have our landmark elements, header, main, and footer.

The {{ content | safe }} line in our main tag will be replaced by the contents of whatever page is using this layout. We can update our index.md to reference this layout by specifying some front matter.

---
title: Home Page
description: This is the home page. It isn't very interesting right now.
layout: layout.njk
---

# Home Page

You are on the home page! Try editing this file to see things change in real time and inspect the HTML to make sure everything is valid.

If we rebuild the site, we should see this home page content combined with our layout HTML to build a complete page.

Cleaning Up After Ourselves

Before we move on to making our HTML more dynamic, let’s put in some cleanup steps to make our lives easier in the future. Whenever eleventy builds our files and dumps them into dist, it will overwrite any matching files or create new ones if they don’t already exist.

This can add clutter if we’re not careful, so let’s create an npm script to delete dist every time we run npm start or npm run build. If we’re using macOS or Linux, we can use the rm -rf command to handle this, but that may not work for Windows, so we’ll install rimraf for cross-platform compatibility. If your preferred OS supports rm -rf, feel free to skip this dependency.

npm install --save-dev rimraf

Then, in our package.json, we’ll create a clean script that will delete dist, and we’ll use “pre” scripts to run it before our start and build scripts.

{
  "scripts": {
    "prestart": "npm run clean",
    "prebuild": "npm run clean",
    "start": "eleventy --serve",
    "build": "eleventy",
    "clean": "rimraf dist"
  }
}

Boilerplate. Check.

Even if we stopped here, this would still be a useful starter template, since we wouldn’t need to run through these installation and configuration steps for a new project. Even if it only saves half an hour of work, that’s 30 minutes that can be spent building the thing rather than writing boilerplate.

Now that our project structure and basic configuration are set up, we can move on to the good stuff! Join us in Part Two, where we will make our layout HTML more dynamic, add some more pages, and get some HTML linting up and running.

Related Content

Check out the 2022 Design Systems Survey!

Over 200 web professionals responded with their design system experiences. Let's learn from this year's findings.

More Details