Search code examples
eleventy

How to create a Tags collection and a Categories collection in Eleventy


I have the following two blocks of code in my .eleventy.js file

    // Tags
    eleventyConfig.addCollection('tagList', collection => {
        const tagsSet = new Set();
        collection.getAll().forEach(item => {
            if (!item.data.tags) return;
            item.data.tags.filter(tag => !['posts', 'all'].includes(tag)).forEach(tag => tagsSet.add(tag));
        });
        return Array.from(tagsSet).sort();
    });

    // Categories
    eleventyConfig.addCollection('categoryList', collection => {
        let catSet = new Set();
        collection.getAll().forEach(item => {
            if (!item.data.categories) return;
            item.data.categories.filter(cat => !['posts', 'all'].includes(cat)).forEach(cat => catSet.add(cat));
        });
        return Array.from(catSet).sort();
    });

In my posts I Have

---
layout: post
title: 'Whatever'
date: '2021-10-12'
permalink: '{{ title | slug }}/index.html'
categories:
    - code
tags:
    - javascript
    - angular
---

And i'm using a template like this to auto generate pages for each Tag (and a similar one for categories).

---
layout: page
eleventyComputed:
    title: All posts tagged '{{ tag }}'
    permalink: /tags/{{ tag }}/
pagination:
    data: collections
    size: 1
    alias: tag
    filter:
        - all
        - nav
        - post
        - posts
        - tagList
    addAllPagesToCollections: true
permalink: /tags/{{ tag }}/
headerButton:
    url: '/blog/'
    text: All posts
---

<ul class="unstyled">
    {%- for post in collections[tag].reverse() -%}
    {%- if not post.data.draft -%}
    <li>{% include 'post-listing.njk' %}</li>
    {%- endif -%}
    {%- endfor -%}
</ul>

For tags, everything is working fine. I get the correct pages generated i.e. /tags/javascript but for categories, its generating pages based on the Tags and not the categories. So instead of /categories/code I'm ending up with /categories/javascript

I'm assuming the issue is with my eleventyConfig.addCollection('categoryList') function but I have no idea why.


Solution

  • Your issue might be in the pagination in your template. You're paginating on the collections object, which is an object mapping tags (a special 11ty front matter attribute) to pages with that tag.

    Since you're creating a list of tags and categories as collection items, you'll want to paginate that collection instead.

    ---
    # ...
    pagination:
        data: collections.tagList # (or collections.categoryList)
        size: 1
        alias: tag
        # ...
    ---
    

    This might pose an issue since you're accessing the posts themselves in the template, so you need a way to map the tag/category back to the list of posts. You can fix this by changing your collections to an Object instead of an Array.

        // Tags
        eleventyConfig.addCollection('tagList', collection => {
            const tagsSet = {};
            collection.getAll().forEach(item => {
                if (!item.data.tags) return;
                item.data.tags.filter(
                    tag => !['posts', 'all'].includes(tag)
                ).forEach(
                    tag => {
                        if (!tagsSet[tag]) { tagsSet[tag] = []; }
                        tagsSet[tag].push(item)
                    }
                );
            });
            return tagsSet;
        });
    
        // Categories
        eleventyConfig.addCollection('categoryList', collection => {
            let catSet = {};
            collection.getAll().forEach(item => {
                if (!item.data.categories) return;
                item.data.categories.filter(
                    cat => !['posts', 'all'].includes(cat)
                ).forEach(
                    cat => {
                        if (!catSet[cat]) { catSet[cat] = []; }
                        catSet[cat].push(item)
                    }
                );
            });
            return catSet;
        });
    

    Now, when you want to access the posts, you can index into collections.tagList (or collections.categoryList).

    {%- for post in collections.tagList[tag] | reverse -%}