Adding categories to Eleventy
I’ve decided to put a bit more love into this blog, I’ve neglected it since the new year. As part of that, I wanted to make a few more changes - namely, I wanted some better navigation to allow me to write different types of content. So, I’ve added a little category list to the site to allow people to search by different tags.
Organising posts
First of all, to identify blog posts, I use a single tag: posts
. I use an 11tydata.json
file in my posts directory that looks like this to ensure every post is automatically tagged correctly:
{
"tags": [
"posts"
],
"permalink": "post/{{ title | slug }}/",
"layout": "post.njk"
}
I also have a draft
tag, that I use to un-publish posts that I’m working on without needing to keep WIP on one machine. I’ll assume that any other tag that an item in the posts
collection has is it’s category, and that a post can have multiple categories.
Getting the category list
So, to generate a list of categories and the number of posts in each category, I’ve added a simple custom collection to my site, called categories
. Here’s the code:
eleventyConfig.addCollection('categories', (collectionApi) => {
const posts = collectionApi
.getFilteredByTag("posts")
.filter(p => !p.data.tags.includes("draft"));
return posts.reduce((tags, post) => {
post.data.tags.filter(tag => tag !== 'posts').forEach(tag => {
if (!tags[tag]) {
tags[tag] = 0;
}
tags[tag]++;
});
return tags;
}, {"All posts": posts.length})
});
It’s fairly simple, even if Javascript’s reduce
is a callback-headache. All we’re doing is getting all of the items in the posts
collection, removing anything tagged as a draft post, and then for each tag we’re first checking if the tag already exists. If it doesn’t exist, we initialise it in our tags object with a count of 0. Then, we increment the tag count by 1. We then also add an extra tag called All posts
, which is the total count of the posts
object.
The output of this function is an object that looks like this:
{
"All posts": 10,
"frontend": 3,
"backend": 2,
"recipes": 4,
"books": 1
}
Displaying categories
Listing the categories is easy, we just need to use our new collection:
<ul>
{% for category, count in (collections.categories) %}
{% if category == "All posts" %}
<li><a href="{{ '/blog' | url }}">{{ category }} ({{ count }})</a></li>
{% else %}
{% set caturl = ["/blog", "category", category] | join("/") %}
<li><a href="{{ caturl | url }}">{{ category }} ({{ count }})</a></li>
{% endif %}
{% endfor %}
</ul>
To actually display a category, Eleventy has an easy guide for this. We just need a bit of customisation to use our blog layout, filter out tags such as drafts, and the categories themselves, and then set our permalink:
---
pagination:
data: collections
size: 1
alias: tag
filter:
- draft
- categories
- all
permalink: /blog/category/{{ tag }}/
layout: blog.njk
eleventyComputed:
pageTitle: Posts Tagged "{{ tag }}"
title: Lewis Dale's Blog
---
{% set taglist = collections[ tag ] %}
{% for post in (taglist | filterDrafts | sortedByDate) %}
{% include "components/blogpost.njk" %}
{% endfor %}
And that’s pretty much it! There’s probably still some work to be done with paginating the tags once I have enough posts to need it.