Paginated collections by tag in Eleventy
Wed, Dec 17, 2025
I want to make Eleventy generate paginated lists of posts for each tag I use. However, I want Eleventy to find all tags and generate the pages dynamically. I definitely don’t want to do my own pagination. But it seems this is impossible.
I came across some blog posts where others have thought about this:
- Double-Pagination in Eleventy
- Eleventy Nested Pagination
- Quick Tip: Zero Maintenance Tag Pages for your Blog
For context, I’m using the tags value only, instead of something else like category.
I give every post the articles tag, and I also include any other relevant keywords.
This is nice because Eleventy automatically makes a collection for each tag.
Some of the problems I encountered:
- You need pagination for the list of articles, but to dynamically make a list of pages you also need pagination, and double pagination isn't supported
- You can dynamically add pages (“templates” in Eleventy speak), but you can’t add pages while using the collections API (it seems it’s too late)
- You need to use the collections API to get all tags
I like the "Double-Pagination in Eleventy" option the most—in particular the first solution, which used a hard-coded list of tags. I think it's not ideal, but I prefer it over implementing my own pagination. And I know there was example code for it, but I have a moral objection here.
However, I've made this solution bearable. You can check that your list of tags contains all the right tags without extras. If you’re missing tags or have extras, throw an error and stop building the site. In the error message you can even include the tags that should be added/removed. You can be confident that the list of tags is always correct.
The relevant part of eleventy.config.js is below. I removed the "articles" tag because all posts have that tag.
function setEquals(a, b) {
return a === b || (a.size === b.size && [...a].every((it => b.has(it))));
}
const knownTags = new Set(['skateboarding', 'software', 'attention']);
eleventyConfig.addCollection('tagsList', collectionApi => {
const tags = collectionApi.getAll()
.flatMap(item => item.data.tags || [])
.reduce((acc, tag) => {
acc.add(tag);
return acc;
}, new Set());
tags.delete('articles');
// check that we know all tags and all known tags are actually used
if (!setEquals(knownTags, tags)) {
const tagsToAdd = tags.difference(knownTags);
const tagsToRemove = knownTags.difference(tags);
throw new Error(`Update list of tags! Add: ${[...tagsToAdd]} Remove: ${[...tagsToRemove]}`);
}
return [...tags];
});
Use the list of tags to dynamically add your templates.
knownTags.forEach(tag => {
eleventyConfig.addTemplate(`tags/${slugify(tag)}.njk`, `<p>All articles with the tag <em>${tag}</em></p>`, {
layout: 'paged.njk',
title: tag,
pagination: {
data: `collections.${tag}`,
reverse: true,
size: 6
}
});
});
You can see that I have pagination values in the front matter, but it's not used in the template. Instead I used the pagination in the layout.
---
layout: secondary.njk
---
<section id="{{ title | lower}}">
<h2>{{ title | title }}</h2>
{{ content | safe }}
{%- for a in pagination.items %}
<article>
<h3>{{ a.data.title }}</h3>
<p class="time">{{ a.data.dateString }}</p>
<p class="summary">{% summarize a %}</p>
<p><a class="button" href="{{ a.url }}">Read more</a></p>
</article>
{%- endfor %}
Bonus: I used the list of tags to make a page that had—you'll never guess—a list of all tags! Is it useful? I don’t know, but now I have it.
---
layout: secondary.njk
title: Tags
---
<section id="all-tags">
<h2>{{ title }}</h2>
<p>All tags used in articles</p>
<ul>
{%- for tag in collections.tagsList %}
<li><a href="/tags/{{ tag | slugify }}">{{ tag }}</a></li>
{%- endfor %}
</ul>
</section>