Building a Website Using 11ty
Ok, I admit. I have a thing for static site generators. It started with Jekyll; after that, Gatsby was my weapon of choice for a while. But new tools keep popping up every month, and my new favorite, without a doubt, is 11ty.
In the first article of my web development in 2022 series, I will show how I use 11ty to create this site.
Most web developers are probably familiar with static site generators by now. They are flexible and powerful tools to create websites that don’t rely on databases, PHP runtimes, or even JavaScript. Instead, they generate plain HTML, CSS, and images just like the web used to be. You can add JavaScript if needed, but the basic experience remains fast, secure, and almost maintenance-free.
But flexible tools can quickly become too complicated, and I feel that Gatsby suffered that fate. Every time I opened one of my Gatsby projects and updated its dependencies, something broke. And even though GraphQL (that Gatsby uses as its data layer) sounded like a cool idea at the time, it may be overkill for a static website.
So when I rebuilt this site, I switched to 11ty, and I haven’t regretted it for a second. This article will guide you through my setup, starting with the simple pages. It is the first article in a series where I will also cover more complex pages, social media, performance, accessibility, and a list of other topics. I will not show you every possible way to do it; instead, I will show you how I’ve done it. If it suits you – great – if not, use it as a starting point.
Getting started
This article will not show you every piece of code needed to create a website. Instead,
I will explain how everything fits together and zoom in on the more exciting parts. So
instead of listing every command needed to get started, I’ve created a complete starter
project that you should download from
GitHub. So, download it, run
npm install
followed by npm start
, and the website should
start. The rest of this, and future, articles will reference files in this project.
Let’s walk through the pieces one by one.
Content
Every website project should start with some content. For 11ty, the content is stored in
Markdown files in the folder
/content
. The actual text of the files is plain Markdown but what makes
static site generators so flexible is that you can combine this with YAML frontmatter to
associate any structured data with the content.
As an example, you’ll find the file 2022-10-02-introduction.md
listed
below. As you can see, it has some content, but it also has metadata such as the layout
to use when rendering the article (more on that later), the title of the article, and
even some tags to organize the articles.
---
layout: ArticleLayout.jsx
title: Part 1, Introduction
tags: [article]
---
Ok, I admit. I have a thing for static site generators. It started with Jekyll; after that, Gatsby
was my weapon of choice for a while. But new tools keep popping up every month, and my new favorite,
without a doubt, is 11ty.
In the first article of my web development in 2022 series, I will show how I use 11ty to create this
site.
---
Most web developers are...
Each Markdown file will create its own page. So if you want to create a page at the url
/cool/
, you create a file called index.md
in the folder
/content/cool
. You can always override the url of a page by adding
permalink: /not-cool/
in the frontmatter data.
A Markdown file is a trigger to create pages. As we’ll see in a later article, one Markdown file can create multiple pages, but no Markdown file = no page.
Images, css and other assets
The next piece of content is images, css and other assets. Different types of assets
need to be handled differently. Images, for instance, should be optimized, resized to
the correct size, and delivered to the browser in the best format possible. For this
reason, I place all assets in the folder /src/assets
.
I will return to this folder in a later article where I’ll discuss performance. For now,
I’ve configured 11ty to simply copy all assets to the output folder. So if you add an
image to /src/assets/image.png
, you can reference it as
/assets/image.png
in your pages.
Layouts
All static site generators use some kind of templating engine to lay out your pages. 11ty is a bit different here since it allows you to choose from many different engines (such as Nunjucks, Liquid, and Handlebars). Coming from Gatsby and the React world, they all looked kind of dated. Fortunately, you can add more engines through plugins, so I use eleventy-hast-jsx. This plugin brings the simplicity of JSX and the full power of JavaScript to your layouts.
Let’s start with the most straightforward layout, the about page in
/src/layouts/AboutLayout.jsx
.
const { Raw } = require("eleventy-hast-jsx")
const Navigation = require("../components/Navigation")
function AboutLayout({ content, page, title }) {
return (
<>
<Navigation url={page.url} />
<main>
<article>
<header>
<h1>{title}</h1>
</header>
<Raw html={content} />
</article>
</main>
</>
)
}
module.exports = {
default: AboutLayout,
data: {
layout: "BaseLayout.jsx"
}
}
There is a lot to unpack here, so let’s start from the top.
Components
Using JSX and JavaScript, you can structure your layouts just like you would any React application – through components. 11ty has a general concept called shortcodes that provide a similar feature across all templating engines. I prefer the React way of doing it, so I’ve just ignored that feature altogether.
This is the /src/components/Navigation.jsx
component.
const items = [
{ href: "/", text: "Blog" },
{ href: "/archive", text: "Archive" },
{ href: "/about", text: "About" }
]
function Navigation({ url = "" }) {
const activeIndex = items.reduce((previousValue, item, index) => {
return url.startsWith(item.href) ? index : previousValue
}, -1)
return (
<nav>
<ul>
{items.map((item, index) => (
<li className={activeIndex === index ? "active" : ""}>
<a href={item.href}>{item.text}</a>
</li>
))}
</ul>
</nav>
)
}
module.exports = Navigation
If you are familiar with React, this should look very familiar. We get our props as arguments to the function and deconstruct them directly. We then use standard JavaScript and JSX to generate the HTML that we want to output.
Note. The goal is to create a static HTML page, so no click handlers or other interactive parts of React are allowed – just plain HTML elements. I may get back to interactive components in a later article.
Components can be composed of other components, so you can create an entire design system using components if you want to.
Data
If you go back to the layout, your eyes will probably stop directly at the function declaration.
function AboutLayout({ content, page, title }) {
This function creates a standard React component that gets its content, title, and a page object from the props, but who provides these props? 11ty! 11ty passes a lot of data to our layouts, including the page object and all data from the Markdown file.
Remember the layout
key in the Markdown file earlier? A Markdown file
triggers a page to be created, and the layout
key specifies which layout to
use. This is what causes the AboutLayout
function above to be invoked at
all, and as part of that invokation, 11ty provides the props.
In this case, content is the text from the Markdown file, the title comes from the
frontmatter data, and the page is a special 11ty object. Data can come from many
different sources though, and the 11ty documentation has an
entire section about this. My suggestion –
test it! Replace the deconstruction in the function with a single
props
variable and print it using
console.log(JSON.stringify(props, null, 2)
. This will give you an idea of
what data you have available.
Base layouts
One final thing to note about layouts is that they can be nested. In the example above,
you may have noticed that just like the Markdown file had a layout property, the layout
itself exported a layout property. In this case, it points to
BaseLayout.jsx
. This way, we can nest layouts. You may have wondered where
I put all the styling and other HTML stuff. This is the place.
const { DOCTYPE, Raw } = require("eleventy-hast-jsx")
function BaseLayout({ content, title }) {
return (
<>
<DOCTYPE />
<html lang="en">
<head>
<meta charSet="utf-8" />
<title>{title}</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/assets/styles.css" />
</head>
<body>
<Raw html={content} />
</body>
</html>
</>
)
}
module.exports = {
default: BaseLayout
}
Building
These are the basic building blocks to getting started. Create your content in Markdown files, add images, css, and other assets to your assets folder and create the layouts you need. If you need a little structure, break up the layouts into components.
The final step is to run npm run build
to create a static website in the
build
folder, copy the folder to your hosting provider of choice, and enjoy
your beautiful website.
—
That’s it. In the next article, I will turn up the complexity a bit by creating the blog and archive pages. I will also give you a few tips and tricks.