Using Eleventy to Generate Static Websites

I started this project one weekend to explore programatically creating static websites. As you might be able to tell, this website hosts a variety of unit converters. While maybe not groundbreaking, I thought they were an interesting topic to explore generating lots of static websites from a small amount of metadata. It's built using Eleventy which takes Nunjucks templates and outputs plain HTML files. These are then hosted on a static S3 bucket and proxied through Cloudflare. In total, it costs me under $1 a month to run the site that now has ~500 pages generated from less than 10 json files.

Content Generation

I started by creating a base file of units and their relative multiplier. For example, for lengths it looks something like
[
{
"unit": "micrometers",
"symbol": "μm",
"multiplier": 0.000001
},
{
"unit": "millimeters",
"symbol": "mm",
"multiplier": 0.001
},
{
"unit": "centimeters",
"symbol": "cm",
"multiplier": 0.01
},
{
"unit": "meters",
"symbol": "m",
"multiplier": 1
},
]
I wanted this to be the only data I needed to maintain in order to add new converters. Eleventy has support for pagination using it's frontmatter information. For example, to list item[1-4] onto two pages of two items each, I could use the following frontmatter
---
pagination:
data: testdata
size: 2
testdata:
- item1
- item2
- item3
- item4
---
<ol>

{%- for item in pagination.items %}
<li>{{ item }}</li>
{% endfor -%}
</ol>
The eleventy docs says you can also use this to generate pages from data. For example, to generate a page for each item in the testdata array, you could use
---
pagination:
data: testdata
size: 1
alias: data
permalink: "pages/{{ data }}/"
---
{{ data }}

Page Generation

However in my case, I wanted to generate pairs of [(item1, item2), (item2, item3), etc] and dynamically generate the page off of the data. This is when I discovered that Eleventy supports javascript frontmatter, instead of just the above yaml. This, combined with a clever "before" callback, allowed me to hack together something like
const config = {
pagination: {
data: "lengths",
size: 1,
alias: "convert",
addAllPagesToCollections: true,
before: function(pages, fullData){
let cross = [];
pages.forEach(page => {
pages.forEach((second, i) => {
if (page != second)
{
cross.push({from: page, to: second})
}
})
})
return cross
}
},
eleventyComputed: {
permalink: comp => makeURL(comp.convert.from.unit, comp.convert.to.unit),
},
layout: "convert_layout.njk"
}
module.exports = { data: () => config }
This eleventy frontmatter allowed me to create a single layout "simple_convert.njk" that then was invoked for each cross product of the units. See an example here or (Days to Seconds). This leads to a lot of generated pages, but it's a lot easier to maintain than having to manually create a page for each unit pair. I also used this to generate the lengths page, which lists all the units and their conversions to each other. This is possible since Eleventy automatically maintains a list of the pages that have been generated.

Closing

Overall, I enjoyed using Eleventy on this project. It's a lot easier to use than I expected and I was able to get a lot of functionality out of it. I'm looking forward to using it on future projects. I was impressed to see how lightweight it was while also enabling complex behavior like the above. I hope this has been helpful to anyone looking to use Eleventy to generate static websites.