Skip to main content

Creating a similar post component with Eleventy

Posted on

When updating my portfolio design, I wanted to create a component that automatically displays similar posts at the bottom of each blog post. Because I couldn't find any tutorials on how to achieve that, I thought it would be a good idea to share my solution.

two similar blog posts in big colored containers at the bottom of each of my posts

There's different ways of defining similar posts, but I decided to go for a simple first version: posts are considered similar to each other if they have one category or more in common. For some posts this list can grow quite long, so I limited the component to only show the two posts with the highest number of common categories.

Filtering posts

The main functionality for this feature is added in the Eleventy config file (most likely called .eleventy.js), where we'll create a custom filter.

eleventyConfig.addLiquidFilter("similarPosts", (collection, path, categories) => {});

The way filters are defined is dependent on which templating language you're using, in my case Liquid. Other variations can be found in the Eleventy filter documentation.

The filter will receive three inputs:

  • collection: the collection of posts that should be filtered
  • path: the path to the active post
  • categories: the categories of the active post

We only want to return posts that have at least one category in common, which I solved this way:

eleventyConfig.addLiquidFilter("similarPosts", (collection, path, categories) => {
    return collection.filter((post) => {
        return post.data.categories.filter(Set.prototype.has, new Set(categories)).length >= 1;
    });
});

This will return a list of posts that have at least one category in common. However, the current post is included in this list as well. We don't want to display the post we're looking at in its own list of similar posts, so it has to be filtered out:

eleventyConfig.addLiquidFilter("similarPosts", (collection, path, categories) => {
  return collection.filter((post) => {
    return post.data.categories.filter(Set.prototype.has, new Set(categories)).length >= 1
      && post.data.page.inputPath !== path;
  });
});

This returns the correct list of similar posts, but not yet sorted by similarity. Using the same way of detecting overlapping categories as above, we can now sort our posts as well:

categories) => {
  return collection.filter((post) => {
    return post.data.categories.filter(Set.prototype.has, new Set(categories)).length >= 1
      && post.data.page.inputPath !== path;
  }).sort((a, b) => {
    return b.data.categories.filter(Set.prototype.has, new Set(categories)).length - a.data.categories.filter(Set.prototype.has, new Set(categories)).length;
  });
});

Which after some code clean-up looks like this:

const getSimilarCategories = function(categoriesA, categoriesB) {
  return categoriesA.filter(Set.prototype.has, new Set(categoriesB)).length;
}

module.exports = function(eleventyConfig) {
  ... // Other configs
  eleventyConfig.addLiquidFilter("similarPosts", function(collection, path, categories){
    return collection.filter((post) => {
      return getSimilarCategories(post.data.categories, categories) >= 1 && post.data.page.inputPath !== path;
    }).sort((a,b) => {
      return getSimilarCategories(b.data.categories, categories) - getSimilarCategories(a.data.categories, categories);
    });
  });
}

Liquid component

Now the only thing left is connecting this to our blog post component. I use Liquid templates, but the principle is the same when using other templating languages.

{% assign similar = collections.sortedPosts | similarPosts: page.inputPath, categories %}
<ul>
  {% for post in similar limit: 2 %}
    <li>
      <a href="{{ post.url }}">{{ post.data.pageTitle }}</a>
    </li>
  {% endfor %}
</ul>

More sources

Hi! 👋🏻 I'm Sarah, a self-employed accessibility specialist/advocate, front-end developer, and inclusive designer, located in Norway.

I help companies build accessibile and inclusive products, through accessibility reviews/audits, training and advisory sessions, and also provide front-end consulting.

You might have come across my photorealistic CSS drawings, my work around dataviz accessibility, or my bird photography. To stay up-to-date with my latest writing, you can follow me on mastodon or subscribe to my RSS feed.

💌 Have a freelance project for me or want to book me for a talk?
Contact me through collab@fossheim.io.

Sign up for notifications and extra content

Subscribe to my newsletter to receive a notification when a new post goes live. I will also send occasional newsletter-only content about front-end development, accessibility and ethical/inclusive design.

You'll need to confirm your email address. Check your spam folder if you didn't receive the confirmation email.

Similar posts

View post archive