Making a weblog with eleventy

⭐ Star Date: Fri Dec 29 2023 ⭐

I've had quite a few blogs over the years. I don't quite remember which was my first. Maybe it was xanga? Or did livejournal come first? I've had more than a few wordpresses and tumblrs over the years. There's nothing wrong with these platforms, and if I could only stick with one blog and one platform over the years I'd probably write more, and have some kind of following. But writing is the hard part, and rebuilding a blog over and over again is the fun part. Just like the allure of a brand new notebook, fresh with possibilities, a new blog carries the same for me. It feels better to start fresh every few years. How long will this one last? Who can say.

So here we are, and as the first post here, I'm obligated to now describe to you how I made it with eleventy. The eleventy docs are much more helpful than I'll be, but sometimes docs are overwhelming and you just want to read a blog post. So here's another to add to the web.

My Goals for the blog

About Eleventy

I heard about eleventy from my colleagues while working at Glitch. It was really neat then, it's even more neat now, as the project continues to grow and develop. Here's a few things that made it the right fit for this project:

It's a static site generator, meaning that it builds all of my blog posts into html files for me. In my day job, I tend to write a lot of single page web apps. This is fine for a big hunk of interactive software that stands behind a login screen. But react and it's like tend to be overkill for building a good old fashioned weblog. I don't have a bunch of state to manage. I have documents to organize and link together. Javascript is slow. It takes a bit to download, then takes another bit to turn itself into html elements on the dom. Already built HTML files on the other hand download quickly and are quick to render, and are great for SEO and accessibility. But I didn't want to write custom html everytime I had a thought. So there we go-- static site generator time.

Ok, ok, but there are hundreds of Static Site Generators. Why Eleventy? Eleventy's website seems to tell you that it's because they are fast to build. I think that's probably very true. But honestly I don't care a lot about how quickly my blog is to build. Like... maybe someday I will care if I ever have 1000s of blog posts to compile into html. But let's be real I'll be very lucky if this baby ever reaches 10 blog posts. This site is more likely going to be landing page that redirects recruiters to my linkedin.

I think for me, the appeal is it's simplicity (which probably is what enables that build performance). It's simple enough that I think I can walk through the basics of how it works right now.

first let's take a look at the file structure before we build it:

blog/
|-- source/
|   |-- layouts/
|   |   |-- index.njk
|   |   |-- post.njk
|   |-- public/
|   |   |-- styles.css
|   |-- posts/
|   |   |-- some-post.md
|   |-- index.md
|-- .eleventy.js
|-- package.json

Pretty small, nice!

The package.json file

Let's open the package.json:

{
  "name": "sarah zinger's weblog",
  "version": "1.0.0",
  "scripts": {
    "build": "npx @11ty/eleventy",
    "start": "npx @11ty/eleventy --serve"
  },
  "devDependencies": {
    "@11ty/eleventy": "2.0.1"
  },
  "dependencies": {}
}

Pretty small! No runtime dependencies! I have 2 build scripts, npm start that I use when running locally, and npm build which builds a production build for me.

Eleventy Config File

The magic happens in the .eleventy.js file. Technically you don't need one, but if you want to do any custom configuration, this is where the magic happens. Here's a simplified version of mine:


module.exports = function (eleventyConfig) {
	eleventyConfig.addPassthroughCopy("source/public");

	return {
		dir: {
			input: "source",
			output: "build",
			includes: "layouts",
		}
	}
};  

All we're really doing here is:

So let's take a look at what the file structure looks like after we run npm run build:

blog/
|-- source/
|   |-- layouts/
|   |   |-- index.njk
|   |   |-- post.njk
|   |-- public/
|   |   |-- styles.css
|   |-- posts/
|   |   |-- some-post.md
|   |-- index.md
|-- build/
|   |-- posts/
|   |   |-- some-post
|   |   |   |-- index.html
|   |-- public/
|   |   |-- styles.css
|   |-- index.md
|-- .eleventy.js
|-- package.json

You can see my some-post.md has become it's own html file, and anything in public is just copied as is.

Layouts and templates

I'll be honest, I don't love templating languages, but eleventy supports many to choose from, and they do make life easier. Most of their docs seem to have a bias for nunjucks so that's what I used. It's used by mozilla, so I imagine it's not going away anytime soon, so far no real complaints. Here's an example of one of my nunjucks layout files:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ title }}</title>
        <link rel="stylesheet" href="/public/styles.css">
    </head>
    <body>
        <main>
            {{ content | safe }}
        </main>
    </body>
</html>

You can do a lot more with it, but I have kept it pretty simple here. When used it's going to take whatever content is there and run it through a "safe" function (aka take this and render it as html, we don't think this is malicious code).

To then use this layout we can add some "front matter" to our post referencing the name of this file. So for example if the layout I just shared above was called "some-template.njk" I could make a markdown file like so:

---
layout: some-template.njk
title: some post
tags: post
---
# Pretend post
blah blah

## can use headers and other markdown

Also html <b>works</b>

then eleventy would create a matching html using that layout to look like this:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>some post</title>
    <link rel="stylesheet" href="/public/styles.css">
  </head>
  <body>
    <main>
        <h1>Pretend post</h1>
        <p>blah blah</p>
        <h2>can use headers and other markdown</h2>
        <p>Also html <b>works</b></p>
    </main>
  </body>
</html>

Collections

Eleventy has concept of "collections". It's basically a big javascript object that contains all of your processed files, sorted by tags.

So for example, in the markdown file example above you may have noticed some meta data including a tag called post. This gives me the ability to then reference all of those files that have been sorted into "post". For example I could write code like this:

<ul> Weblog Postings:
{%- for post in collections.post -%}
  <li><a href="{{post.url}}">{{ post.data.title }}</a></li>
{%- endfor -%}
</ul>

and it will render all of my posts like so:

Getting fancy

Ok that's the basics for getting started with a blog for eleventy, but I found there were a few other things I wanted to set up right away:

Syntax Highlighting

I hope to occasionally write things about code here, and when I do I want it to look pretty. Eleventy has some tooling to make this easy using Prism. Here's how I did it:


const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");

module.exports = function (eleventyConfig) {
	eleventyConfig.addPassthroughCopy("source/public");
    eleventyConfig.addPlugin(syntaxHighlight);
	return {
		dir: {
			input: "source",
			output: "build",
			includes: "layouts",
		}
	}
};  

Drafts

I wanted some way to be able to write and preview drafts, without building them in production. Fortunately 11ty has a handy tutorial for that. All you have to do is add a few lines to your config file, and then anytime you want a post to be unpublished you simply need to add draft: true to the front matter meta data like so:

---
layout: post.njk
title: draft
tags: post
draft: true
---
a test draft

and the post will only build when you're running it locally

Table of Contents

I tend to think in terms of headers sometimes. I'm not sure that's useful when reading, but it's how I think when writing. When I write papers for work, I often use the Table of Contents feature from google docs, and I kind of wanted something similar. And wouldn't you know it, someone has already needed something like this too! Check it out another awesome eleventy plugin to the rescue!

Custom filters

One last thing, I suspect there is a "better" way to do this but when I wanted to show the day's date I noticed it was a bit wordy by default.

Writing:

{{page.date}}

shows as:

Fri Dec 29 2023 01:24:55 GMT+0000 (Coordinated Universal Time)

I found out one way to do this with nunjucks is to write a custom filter. Here's how I did it with eleventy:

eleventyConfig.addFilter("starDate", (function (datestring) {
    return ` ⭐ Star Date: ${new Date(datestring).toDateString()}`;
}))

So now I can just write

{{ page.date | starDate }}

and I'll get

 ⭐ Star Date: Fri Dec 29 2023 ⭐

Anyway that's all for now!