Eleventy - Group posts by year
A common organisation pattern for a blog or an “archives” page is to group posts by year.
Let’s create a blog with posts grouped by year in descending order (newest to oldest). The page will look like this:
This is actually tricky to pull off! Why is that?
The crux of the problem
If you are using Nunjucks as a template language, there is a groupby
filter. If you add a year
property to your posts collections through computed data, then you can use the groupBy
filter in Nunjucks to get the grouped dataset you require.
The groupby
filter will return an Object
like this:
The properties (keys) are sorted in ascending order (oldest to newest). If you are happy with that, then your page template for your blog page is pretty straightforward:
However, I think that most of the time you want the data sorted in the opposite order. This is where things get tricky!
The crux of the problem is that you need both the properties (years) and the values (array of yearly posts) sorted in reverse order. Doing this on an Object
is cumbersome and error-prone.
We want this:
If you want to do the sorting in Nunjucks, you might expect that using the reverse
filter on both the property and value will give you the desired result – it does not! Chris Kirk Nielsen describes this issue in detail and how to get around it in Nunjucks in his tutorial. Chris explains the code really well, but it requires too much explanation in my opinion! It is probably better to go in another direction.
I suggest using a Map
instead and doing the work in JavaScript. It is more explicit and more efficient.
A Map
is preferable over an Object
in many cases because:
- The keys of an
Object
must be either aString
or aSymbol
. AMap
’s keys can be any value. We don’t need to worry aboutyear
being a number; - Maps perform better when frequent additions are being made;
- A Map is an
iterable
, so it can be directly iterated everywhere (for
loops in Nunjucks do permit iteration over an object); - The keys in
Map
are ordered by insertion. The keys (properties) of anObject
are ordered similarly, but in prior versions of JavaScript, they were not. I think it’s best not to rely on property order of anObject
.
You can read Objects vs Maps on MDN for a more detailed comparison.
Long story short, a Map will save you grief if you use it!
The code
There is a short and elegant solution to this if you are happy to stick with Node v21+. If you are running an older version of Node, I outline an alternative version that will work across all versions of Node.
Here is an abbreviated outline of the project:
Our posts files live in content/posts and we use a mixture of styles to test them out!
The blog-desc.njk template file will create our blog page with our grouped posts in descending order.
The Node v21+ version - use native Map.groupBy
There is a relatively new builtin function to do the dirty work for us – Map.groupBy
. This function is part of the Array grouping ECMAScript proposal and made it into browsers and Node last year.
Add a collection in the elventy configuration file
We will create a new collection in our eleventy config (eleventy.config.js). I will call it postsByYearDescVersion21
to be super explicit for this demo. I use thegetFilteredByGlob()
function to create an array of all posts, which I will sort and group.
Here is the code:
You can see that we sort the posts
array before we use Map.groupBy
. This ensures that the keys and values will be sorted as we expect. It produces a Map object as below:
The blog template - blog-desc.njk
In blog-desc.njk, we can loop through our postsByYearDescVersion21
collection and output the grouped posts.
The general Node version - write your own groupBy
function
We want the equivalent of the Map.groupBy
function that return a Map
object. Most utility libraries like lodash return an Object
. We will write our own groupBy
function!
Create a groupBy
function in utilities.js
You can check out the question “Most efficient method to groupby on an array of objects” on StackOveflow for a discussion on this. I have lifted mortb’s answer here, you can go for more efficient but less readable alternative if you wish!
We will add our function to a file called utilities.js to keep things organised.
Create a collection in the eleventy configuration file
Again, we will create a new collection in our eleventy config. This time we will call it postsByYearDesc
. We import (require
) our groupBy
function and use it on the sorted posts
array.
The blog template - blog-desc.njk
Our blog-desc.njk template is identical to the previous implementation, just the collection name is different.
Source code
You will find the code in the group-post-by-year
subfolder of the https://github.com/robole/eleventy-tutorials repo. Both versions are included. The general Node version is the default.
Can you give the repo a star to indicate that this was a worthwhile tutorial please? 🌟
Final words
In this tutorial, I demonstrated how to group posts by year with a Map
in JavaScript. The code is more efficient and more comprehensible than the alternatives I have seen! Whatever templating language you use, I recommend following this pattern.