text: The author selected the Internet Archive to receive a donation as part of the Write for DOnations program.
  Eleventy (also known as 11ty) is a static site generator (SSG) for building websites. It was launched in 2017 by Zach Leatherman as a JavaScript-based alternative to Jekyll, one of the first mainstream SSGs, which is written in Ruby. Eleventy has gained a reputation as one of the most flexible and performant options for building static websites, leading to steadily rising adoption rates in the Jamstack ecosystem.
  It’s important to note that Eleventy is not a JavaScript framework, and it does not include any client-side JavaScript. It takes template files specified in HTML, Markdown, or your choice of templating language, and outputs a complete, static website ready to be deployed to a web server of your choice.
  While most other SSGs are restricted to just one templating language, Eleventy supports multiple templating languages, such as HTML, Liquid, Markdown, Nunjucks, Handlebars, moustache, EJS, Haml, Pug, etc., and you can even combine them in the same project. This flexibility is one of the things that makes Eleventy stand out from its competition.
  In this tutorial, you’ll develop a static website from scratch with Eleventy and deploy it to DigitalOcean’s App Platform for free.
  Prerequisites
  To complete this tutorial, you will need:
  Step 1 — Setting Up the Project
  Unlike competitors such as Jekyll and Hugo, Eleventy does not provide a way to scaffold a new project, so you’ll need to create a regular Node.js project, and then add Eleventy as a dependency.
  The first step is to launch the terminal on your computer, create a new directory somewhere on your filesystem, and change into it as shown below.
  - mkdir eleventy-blog
  - cd eleventy-blog
  At the root of the
  eleventy-blog directory, initialize the project with a
  package.json file with
  npm init -y, and install Eleventy as a development dependency by passing the
  -D flag to the
  install subcommand.
  - npm init -y
  - npm install -D @11ty/eleventy
  Once the Eleventy package is installed, inspect the contents of your project directory with
  ls. It will contain a
  package.json file, a
  package-lock.json file, and a
  node_modules directory.
  The output should look similar to this:
  Outputnode_modules package-lock.json package.json
  Open the
  package.json file in your favorite text editor, then replace the existing
  scripts property with the highlighted lines below.
  eleventy-blog/package.json
  { "name": "eleventy-blog", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "eleventy", "start": "eleventy --serve" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@11ty/eleventy": "^0.12.1" } }
  There are two scripts here:
  build for building the website files, and
  start for running the Eleventy web server on.
  Save the file, and then run the command below to build the website:
  The output should look similar to this:
  Output> eleventy-blog@1.0.0 build > eleventy Wrote 0 files in 0.04 seconds (v0.12.1)
  The output indicates that you haven’t created any files yet, so there is nothing to build. In the next step, you’ll begin adding the necessary files that are needed for the website.
  The final directory structure of the project you’ll be working on is shown below. You’ll start from an empty directory, and incrementally add new features until you arrive at this structure.
  . ├── about │ └── index.md ├── css │ └── style.css ├── _data │ └── site.json ├── _includes │ ├── layouts │ │ ├── base.njk │ │ ├── page.njk │ │ └── post.njk │ └── nav.njk ├── index.njk ├── node_modules ├── package.json ├── package-lock.json ├── posts │ ├── first-post.md │ ├── second-post.md │ └── third-post.md └── _site ├── about │ └── index.html ├── css │ └── style.css ├── index.html └── posts ├── first-post │ └── index.html ├── second-post │ └── index.html └── third-post └── index.html
  In this step, you created a Node.js project and added Eleventy as a dependency. In the next step, you’ll choose a templating language.
  Step 2 — Choosing a Templating Language
  For the purpose of this tutorial, we’ll use the Nunjucks template, a common choice for many Eleventy projects. (Depending on your preference, you could also choose a different templating language.)
  In the root of your project directory, create an
  index.njk file and open it in your text editor. Add a “Hello world” message to the file as shown below, then save the file.
  eleventy-blog/index.njk
  <h1>Hello, world!</h1>
  Once saved, run the build command again. It will convert the
  index.njk file to an
  index.html file and output it into a new
  _site directory at the root of the project.
  The output should look similar to this:
  Output> eleventy-blog@1.0.0 build > eleventy Writing _site/index.html from ./index.njk. Wrote 1 file in 0.08 seconds (v0.12.1)
  At this point, you can view the website in the browser by starting a development server at as shown below.
  The output should look similar to this:
  Output> eleventy-blog@1.0.0 start > eleventy --serve Writing _site/index.html from ./index.njk. Wrote 1 file in 0.08 seconds (v0.12.1) Watching.. [Browsersync] Access URLs: ----------------------------------- Local: External: ----------------------------------- UI: UI External: ----------------------------------- [Browsersync] Serving files from: _site
  If you wish to use a different port, you can specify it through the
  --port option, as shown here:
  - npm start -- --port 4040 to set a different port
  The
  -- separator in the command above is used to distinguish the parameters passed to
  npm command itself from those passed to the script. After starting the development server, open in your web browser to see the site in action. You can exit the server at any time by pressing
  Ctrl + C on your keyboard.
  In this step, you used Nunjucks as the templating language and began building a website. In the next section, you’ll learn about layouts in Eleventy and create a homepage for the website.
  Step 3 — Creating the Homepage
  In order to make your project more flexible and scalable from the start, you’ll need to create a base template that will be applied to all the pages of the site. Conventionally, this is called the
  base layout, and it needs to go into a
  layouts directory nested within an
  _includes directory. Create the
  _includes/layouts directory using the command below:
  - mkdir -p _includes/layouts
  In the
  _includes/layouts directory, create a
  base.njk file and open it in your text editor. Copy and paste the following code into the file. This is basic HTML5 boilerplate that will serve as the foundation for all the pages on the website.
  _includes/layouts/base.njk
  <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ title }}</title> </head> <body> <header> <h1>{{ title }}</h1> </header> <main> {{ content | safe }} </main> </body> </html>
  The contents in double curly braces are Nunjucks variables that will be replaced accordingly when a derivative page is being built. The
  {{ title }} variable will be supplied through the page’s front matter block while the
  {{ content }} will be replaced with all incoming page content that is not part of the front matter. The latter is piped through the
  safe filter to prevent it from being escaped.
  Return to the
  index.njk file in your project root, and modify it as shown below:
  eleventy-blog/index.njk
  --- title: Homepage layout: layouts/base.njk --- <h1>Welcome to this brand new Eleventy website!</h1>
  The contents on either side of the triple dashes constitute the front matter of the file, while the rest of the file is what will be passed to your layouts as its
  content. In the front matter, the
  title and
  layout of the file are specified accordingly.
  If your development server is still running, head over to your site’s localhost URL to view the changes, or start the server first with
  npm start before attempting to view it in a web browser.
  As you can see from the above screenshot, the
  base.njk template has taken effect on the homepage.
  In this step, you added a base template for the pages of your site, and created a homepage. However, it doesn’t yet have any styling beyond the browser defaults. In the next section, you’ll improve the design of the website by adding a navigation menu.
  The
  _includes directory is where you’ll place the different components of the website. The contents of this directory are partial files that can be placed in your layout files to facilitate reuse. In this section, you’ll create the navigation menu as a partial, and include it in the
  base layout.
  In the
  _includes directory, create a new file called
  nav.njk. Open it in your editor and populate it with the code below. It is the markup for the top navigation bar, and it includes the title of the site as well as links to the homepage and a yet-to-be created “About” page.
  eleventy-blog/_includes/nav.njk
  <nav class="navbar is-light" role="navigation" aria- <div class="navbar-start"> <div class="navbar-item has-text-weight-bold"> My Eleventy Blog </div> </div> <div class="navbar-end"> <a href="" class="navbar-item"> Home </a> <a href="" class="navbar-item"> About Me </a> </div> </nav>
  Save and close the
  nav.njk file, and open the base template file (
  _includes/layouts/base.njk) in your editor. Go ahead and include the new navigation partial in this file through the
  include syntax shown below:
  eleventy-blog/_includes/layouts/base.njk
  <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>{{ title }}</title> </head> <body> <header>{% include "nav.njk" %}</header> <main> {{ content | safe }} </main> </body> </html>
  In the browser, the site should look like this:
  When you check the home page once again, the navigation menu should show up just like in the screenshot above. The title of the site “My Eleventy Blog” is hardcoded into the
  nav partial, but this is suboptimal because it’s likely that you will repeat the title elsewhere on the site, and changing it later becomes tedious since you’ll then have to find each place it was used.
  A better approach is to supply this value through a global data file. These are JSON files placed in a
  _data directory at the project root that provide global data accessible to all template files. At the project root, create a
  _data directory, followed by a
  site.json file within it. Open
  site.json in your text editor and specify the site’s title using the code below.
  eleventy-blog/_data/site.json
  { "title": "My Eleventy Blog", "url": "", "language": "en-US", "description": "A simple blog with awesome content" }
  At this point, you can save and close the file, then return to the
  nav.njk file in the
  _includes directory and replace the hardcoded site title with the appropriate variable.
  eleventy-blog/_includes/nav.njk
  . . . <div class="navbar-item has-text-weight-bold"> {{ site.title }} </div> . . .
  The site should look exactly the same as before, but this small change makes setting and updating global data much easier. One thing to note about global variables is that they are scoped to the name of the JSON file, which is why we used
  {{ site.title }} above. You can create other data files as needed, and use them in your templates. For example, you can have an
  author.json file that contains your personal details such as your name, bio, and links to your social media profiles. Such data could then be accessed through variables (such as
  {{ author.bio }}) on any page of the website.
  Return to your
  index.njk file at the project root and update its contents as shown below so that it uses the site title and description:>
  In the browser, the site should look like this:
  In this step, you added a navigation menu to the website. However, the site is using default styling. In the next section, you’ll style the website using the Bulma CSS framework, which provides flexbox-based frontend components for building responsive websites.
  Step 5 — Adding a Stylesheet
  At the moment, Eleventy does not recognize CSS files for auto-inclusion in the build directory, so a few extra steps are needed to get this working. Specifically, you’ll need to create a stylesheet directory, and ensure that it is copied over to the build output (
  _site) when the site is built. You’ll also need to ensure that modifying a stylesheet triggers a rebuild and automatic refreshing in the web browser. You can achieved this by creating a configuration file for Eleventy.
  At the project root, create a
  css folder followed by a
  style.css file within it. Open
  style.css and import the Bulma CSS framework by using the code below:
  eleventy-blog/css/style.css
  @import "";
  Save the file.
  Next, create an
  .eleventy.js file in your project root. This is the configuration file for Eleventy, similar to
  _config.yml files in Jekyll projects. Note that this file will be hidden in your filesystem since it’s prefixed with a period. You’ll need to use
  ls -a to get it to show up when listing the directory’s contents.
  Open
  .eleventy.js in your text editor and paste the following to include the
  css directory in the build, and also to watch the folder for changes:
  eleventy-blog/.eleventy.js
  module.exports = function (eleventyConfig) { // Copy the `css` directory to the output eleventyConfig.addPassthroughCopy('css'); // Watch the `css` directory for changes eleventyConfig.addWatchTarget('css'); };
  At this point, you need to stop the server with
  Ctrl+C, and start it again with
  npm start before the changes take effect. You’ll need to do this every time you modify the configuration file.
  If you check the site in your browser right now, you won’t notice any changes. That’s because the stylesheet has not yet been linked in any template. Go ahead and add it to the
  base.njk template as shown below.
  eleventy-blog/_includes/layouts/base.njk
  <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="" /> <title>{{ title }}</title> </head> <body> <header>{% include "nav.njk" %}</header> <main> {{ content | safe }} </main> </body> </html>
  After saving the file, the styles should kick in immediately.
  In this step, you added styling to the website using the Bulma CSS framework. In the next step, you’ll expand the site by creating an “About” page.
  Step 6 — Creating an About Page
  At the moment, there is a link to a non-existent “About” page in the navigation menu. You’ll change that by creating a unique layout for all static pages, and afterward, the “About” page itself. In the
  _includes/layouts folder, create a
  page.njk file. This will be the layout for all static pages on the site.
  Open the new file in your editor and populate it with the code below. The front matter
  layout property is used to indicate that the
  page layout should inherit from the previously created
  base.njk template. This is known as layout chaining, and it allows us to reuse a template while adding unique elements that are specific to the new template, which helps avoid unnecessary repetition of basic site structures.
  eleventy-blog/_includes/layouts/page.njk
  --- layout: layouts/base.njk --- <article class="page-layout"> <div class="container"> <div class="columns"> <div class="column is-8 is-offset-2"> <div class="content mt-5"> <h1>{{ title }}</h1> {{ content | safe }} </div> </div> </div> </div> </article>
  Now that you have a page layout, you can create the “About” page. To do this, create a directory at the project root called
  about, and add a new
  index.md markdown file within it.
  Add the following code into the file:
  eleventy-blog/about/index.md
  --- title: About Me layout: layouts/page.njk --- I am a person that writes stuff.
  After saving the file, go to. The page should load correctly with the specified layout.
  Creating other pages, such as a contact page or newsletter page, can be done in the same way: create a directory with the name of the page, then add an
  index.md file at the root of the new directory. You can also use an HTML or Nunjucks file instead of Markdown if you prefer.
  In this step, you created a unique layout for static pages and added an “About” page to the site. In the next section, you’ll create and process blog posts on an Eleventy website.
  Step 7 — Creating Posts
  Creating a blog post is very similar to creating a page. You’ll start by creating a directory called
  posts at the project root to keep all posts.
  However, you will create a different layout for posts. It’s going to be similar to the layout for
  pages, but you will include a date to differentiate it. In a real-world project, it’s likely that you’ll want different layouts for posts and pages, so it’s good practice to create a new layout for each one.
  In the
  _includes/layouts directory, create a new
  post.njk file and open it in your text editor. Paste the code below into your
  post.njk layout file.
  eleventy-blog/_includes/layouts/post.njk
  --- layout: layouts/base.njk --- <section class="page-layout"> <div class="container"> <div class="columns"> <div class="column is-8 is-offset-2"> <article class="content mt-5"> <h1 class="title">{{ title }}</h1> <p class="subtitle is-6"> Published on: <time datetime="{{ page.date }}">{{ page.date }}</time> </p> {{ content | safe }} </article> </div> </div> </div> </section>
  Similar to the
  page.njk template, the post template extends the base template with additions that make sense for posts (such as the date of publication).
  To use this template, create a new file in your
  posts directory called
  first-post.md, and open it in your text editor.
  Paste the following contents into the
  first-post.md file:
  eleventy-blog/posts/first-post.md
  --- title: My First Blog Post description: This is the first post on my blog tags: post date: 2021-06-19 layout: layouts/post.njk --- You’ll find this post in your `posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `eleventy --serve`, which launches a web server and auto-regenerates your site when a file is updated.
  Save the file, then head over to in your browser. Notice how the URL corresponds to the location of the file in the project (excluding the extension). This is how URLs are handled by default, but they can be changed to some other format through the permalink key.
  The post is displayed correctly, but notice how the date is currently formatted. This date format is difficult for users to read, but Eleventy provides no built-in formatting options for dates, unlike most other SSGs. This means that you have to use an external package to get a more human readable format in Eleventy.
  In this step, you created a unique layout for blog posts and added a blog post to the site. In the next section, you’ll create a custom filter that helps with date formatting.
  Step 8 — Using Filters in Eleventy
  Eleventy supports filters for transforming content in various ways. For example, the
  safe filter used earlier prevents the escaping of HTML content, and there are other built-in ones like
  slug for transforming text into URL-friendly strings. You can also add your own custom filters that can be used in any template. These customizations can be made through the configuration file.
  Go ahead and add a universal filter for formatting dates so that it can be used in the post template. Start by installing Luxon, a lightweight JavaScript library for date formatting:
  Afterward, open the
  .eleventy.js config file, and update its contents as follows:
  eleventy-blog/.eleventy.js
  const { DateTime } = require('luxon'); module.exports = function (eleventyConfig) { // Copy the `css` directory to the output eleventyConfig.addPassthroughCopy('css'); // Watch the `css` directory for changes eleventyConfig.addWatchTarget('css'); eleventyConfig.addFilter('readableDate', (dateObj) => { return DateTime.fromJSDate(dateObj, { zone: 'utc' }).toFormat( 'dd LLL yyyy' ); }); };
  The highlighted lines describe how to add a custom filter to Eleventy. First, you need to import whatever objects you need from any external libraries. The
  DateTime type from Luxon provides several methods for formatting purposes.
  Adding a filter involves calling the
  addFilter() method provided by the
  eleventyConfig argument. It takes the filter name as its first argument, and the callback function is what will be executed when the filter is used. In the above snippet, the filter is called
  readableDate, and the callback function is used to format a date object using the provided date tokens. This will yield a date in the following format: 19 Jul 2021.
  Save the config file and restart the server so that the changes take effect. Then use the
  readableDate filter in the post template as shown below to format the post date according to the specified layout.
  eleventy-blog/_includes/layouts/post.njk
  . . . <p class="subtitle is-6"> Published on: <time datetime="{{ page.date }}">{{ page.date | readableDate }}</time> </p> . . .
  Once you open the post in your browser, you’ll notice that the date formatting has been updated.
  In this step, you added a filter to change the date formatting on blog posts. In the next section, you’ll display a list of posts on the homepage, as is conventional on most personal blogs.
  Step 9 — Displaying Posts on the Homepage
  To make it easier for visitors to your site to discover the posts on the blog, it’s a good idea to list them on the homepage. You will use Eleventy’s collections feature in order to implement this functionality.
  Before you proceed to update the
  index.njk file, you’ll need to create at least three additional posts to showcase on the homepage. You can copy the first post into new files, and change the title and description for each of them. Once you’re through with that, update your
  index.njk file as shown below:> <section class="postlist mt-3 pt-3"> <div class="container"> <h2 class="title has-text-centered mt-3 mb-6">Recent posts</h2> <div class="columns"> {% for post in collections.post | reverse %} {% if loop.index0 < 3 %} <div class="column"> <div class="card"> <header class="card-header"> <p class="card-header-title"> {{ post.data.title }} </p> </header> <div class="card-content"> <div class="content"> {{ post.data.description }} </div> </div> <footer class="card-footer"> <a href="{{ post.url }}" class="button is-fullwidth is-link card-footer-item">Read article</a> </footer> </div> </div> {% endif %} {% endfor %} </div> </div> </section>
  The
  for loop construct in the above snippet is from the Nunjucks templating language, and its one of the ways to iterate over a collection in Eleventy. It starts with
  {% for post in collection.post | reverse %} and ends with
  {% endfor %}. The
  post collection is created by Eleventy and consists of any page that has its
  tags front matter property set to
  The
  reverse filter is used here so that the iteration starts from the most recent post instead of the default order, which puts older posts first. Within the loop, the output of the collection is limited to three items, and the
  post local variable is used to access the title, description, and URL of each post in the collection.
  After saving the file, go to the homepage in the browser to see the results. It should look similar to the screenshot below.
  In this step, you created additional blog posts and used Eleventy’s collections feature to list them on the website’s homepage. You now have a styled website with a homepage, an “About” page, and some posts. Next, you’ll deploy it to production through GitHub and DigitalOcean’s App Platform.
  Step 10 — Pushing the Website to GitHub
  Before you can deploy your code to DigitalOcean’s App Platform, you need to get your site in a Git repository, and push that repository to GitHub. The first thing to do is initialize a Git repo in your project directory:
  Next, create a
  .gitignore file at the project root so that you can exclude the contents of the
  node_modules and
  _site directories from being tracked in the Git repo.
  .gitignore
  node_modules _site
  After saving the file, run the commands below to add all the project files to the staging area, then make your initial commit:
  - git add -A
  - git commit -m "Initial version of the site"
  The output should look similar to this:
  Output[master (root-commit) e4e2063] Initial version of the site 15 files changed, 6914 insertions(+) create mode 100644 .eleventy.js create mode 100644 .gitignore create mode 100644 _data/site.json create mode 100644 _includes/layouts/base.njk create mode 100644 _includes/layouts/page.njk create mode 100644 _includes/layouts/post.njk create mode 100644 _includes/nav.njk create mode 100644 about/index.md create mode 100644 css/style.css create mode 100644 index.njk create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 posts/first-post.md create mode 100644 posts/second-post.md create mode 100644 posts/third-post.md
  Navigate to GitHub, log in with your profile, and create a new empty repository for your project called
  eleventy-blog (it can be public or private). Once the GitHub repo is created, copy the link to the repo, and add it as a remote location for your project in the terminal:
  - git remote add origin
  Before you push your changes to the remote location, rename the default branch to
  main to match what GitHub expects:
  Finally, run the command below to push the
  main branch to GitHub. Enter your GitHub account credentials when prompted.
  Note: If two-factor authentication is enabled for your GitHub account, you’ll need to use a personal access token or SSH key when accessing GitHub on the command line. For more information, see Using two-factor authentication with the command line.
  The output should look similar to this:
  OutputEnumerating objects: 23, done. Counting objects: 100% (23/23), done. Delta compression using up to 4 threads Compressing objects: 100% (19/19), done. Writing objects: 100% (23/23), 64.41 KiB | 2.38 MiB/s, done. Total 23 (delta 3), reused 0 (delta 0) remote: Resolving deltas: 100% (3/3), done. To * [new branch] main -> main
  In this step, you added your site to a Git repository, which you then pushed to GitHub. You are now ready to deploy your website to DigitalOcean’s App Platform.
  Step 11 — Deploying to DigitalOcean with App Platform
  Go ahead and log in to your DigitalOcean account, then head over to and click the green Create button on the top right. Choose the Apps option in the dropdown, then, on the resulting page, select GitHub as your source.
  You’ll be redirected to GitHub and prompted to give DigitalOcean access to your repositories. You can choose all repositories, or just the ones you wish to deploy. Click Install and Authorize once you’ve made your choice. You should be redirected back to the Choose Source page once again.
  On the Choose Source page, select GitHub, then choose the
  eleventy-blog repository from the Repository dropdown. Ensure that the selected branch is
  main, and the checkbox to autodeploy code changes is ticked, then click Next to continue.
  On the next page, your project will be auto detected as a Node.js project. You may need to change its Type to Static Site, and the Output Directory to
  _site as shown in the screenshot below. Click Next to continue once everything matches.
  Name your static site, and click Next to go to the Finalize and Launch screen.
  Static sites are free, so retain the default Starter selection under Plans, and press Launch Starter App at the bottom of the page.
  Your site will build immediately, and you should see a success message after a few minutes.
  By default, your app will be given a sub-domain on
  ondigitalocean.app. Now that your site is deployed, you can visit the provided URL to view the live website in your browser. You can also register a custom domain for your site by following How to Manage Domains in App Platform.
  Conclusion
  In this tutorial, you built a static site with Eleventy and deployed to DigitalOcean’s App Platform. Moving forward, you can make changes to your website locally, push the changes to GitHub, and DigitalOcean will automatically update the live site. You can go to the app dashboard to see the progress of the build at any time.
  To build on what you’ve achieved in this tutorial, visit the Eleventy docs to learn more about how you can customize your site’s templates, and add features like responsive images, syntax highlighting, and caching. If you’d like to get started quickly without creating your own theme from scratch, check out the starter projects page.