Table of Contents

Posts and collections

A post is not a page with a different word on it. It belongs to a collection, so other pages can ask for it, and it gets its own address rule so posts sit together under one segment.

Three files, and the first two are what make the third a post.


Register the collection

A collection is a named list of content that Eleventy hands to your templates. It is the convenient way to group pages that belong together, and to enrich them along the way: you decide what goes in, in what order, and you can attach whatever else the list needs before a template ever sees it.

Open eleventy.config.js and add one:

 export default async function (eleventyConfig) {
 	await eleventyConfig.addPlugin(baseline(settings));
+
+	eleventyConfig.addCollection('posts', (collectionApi) =>
+		collectionApi.getFilteredByGlob('src/content/**/posts/*.md')
+	);
 }

Every Markdown file in a posts/ folder is now in collections.posts, which is what the homepage loop was reaching for. The ** matters: it keeps the collection working if the folder moves, which it does when you add a second language.

The function is where the enriching happens when you need it. It receives every piece of content in the project and returns the list you want, so sorting by something other than date, dropping drafts, or pulling posts from several folders into one list are all changes to this one function rather than edits spread across your files.


Give posts their own address rule

Create src/content/posts/posts.11tydata.js:

export default {
	permalink: function ({ slug, page }) {
		return `/posts/${this.slugify(slug || page.fileSlug)}/`;
	}
};

The same trick as pages.11tydata.js, aimed one folder over. It returns /posts/${slug}/ rather than /${slug}/, so posts sit under a segment of their own.

It also falls back to page.fileSlug, the filename, if a post ever arrives without a slug.


Write your first post

Create src/content/posts/1970-01-01-hello-world.md:

---
title: 'Hello world!'
slug: 'hello-world'
description: 'The first post on a brand new site.'
---

Welcome to your new site. This is your first post. Edit it or delete it, then
start writing.

The date is in the filename, and the front matter is shorter for it. Eleventy reads a leading YYYY-MM-DD- as the file's date, so this post is dated the first of January 1970 without a date: field. It also strips that prefix from page.fileSlug, so the filename and the slug agree and the permalink rule lands the post at /posts/hello-world/.

Pages did not need a date, because nothing sorts them. Posts are sorted, and this is what they are sorted by. Putting it in the name also keeps posts in date order in your editor's file list.

Reload the homepage. The empty list has one entry in it now, with a date next to it.


Write something you are not ready to publish

Now give yourself something that should not survive a build. Create src/content/posts/1991-08-06-not-finished.md:

---
title: 'Not finished yet'
slug: 'not-finished'
description: 'A post that is not ready to be read.'
draft: true
---

Still thinking about this one.

It behaves like any other post for now. Leave it there. Run it, then build it is where it disappears.


Have a look at it

There is enough now to see something:

npm start

Open http://localhost:8080/. Four URLs work: the homepage, /about/, /posts/hello-world/, and the draft at /posts/not-finished/. Drafts render while the server is running, which is the whole point of marking one rather than deleting it.

You will find your words in the browser's default font, with nothing around them and nothing styling them. That is not a mistake. Nothing has told Eleventy what a page should look like yet, so it renders your Markdown and stops.

The shell those words belong in is the next page.