Collections (Using Tags)
While pagination allows you to iterate over a data set to create multiple templates, a collection allows you to group content in interesting ways. A piece of content can be a part of multiple collections, if you assign the same string value to the tags key in the front matter.
Take care to note that tags have a singular purpose in Eleventy: to construct collections of content. Some blogging platforms use Tags to refer to a hierarchy of labels for the content (e.g. a tag cloud).
Contents Jump to heading
A Blog Example Jump to heading
For a blog site, your individual post files may use a tag called post, but it can be whatever you want. In this example, mypost.md has a single tag post:
---
tags: post
title: Hot Take—Social Media is Considered Harmful
---This will place this mypost.md into the post collection with all other pieces of content sharing the post tag. To reference this collection and make a list of all posts, use the collections object in any template:
Always prefer…
<ul>
{%- for post in collections.post -%}
  <li>{{ post.data.title }}</li>
{%- endfor -%}
</ul><ul>
{%- for post in collections.post -%}
  <li>{{ post.data.title }}</li>
{%- endfor -%}
</ul>exports.render = function(data) {
  return `<ul>
    ${data.collections.post.map(post => `<li>${post.data.title}</li>`).join("\n")}
  </ul>`;
};Using an [aria-current] attribute for on the current page 
				Jump to heading
				
			
Compare the post.url and special Eleventy-provided page.url variable to find the current page. Building on the previous example:
Always prefer…
Background: aria-current="page" tells assistive technology, such as screen readers, which page of a set of pages is the current active one. It also provides a hook for your CSS styling, using its attribute selector: [aria-current="page"] {}.
The Special all Collection 
				Jump to heading
				
			
By default Eleventy puts all of your content (independent of whether or not it has any assigned tags) into the collections.all Collection. This allows you to iterate over all of your content inside of a template.
Link to all Eleventy generated content Jump to heading
Always prefer…
<ul>
{%- for post in collections.all -%}
  <li><a href="{{ post.url }}">{{ post.url }}</a></li>
{%- endfor -%}
</ul><ul>
{%- for post in collections.all -%}
  <li><a href="{{ post.url }}">{{ post.url }}</a></li>
{%- endfor -%}
</ul>exports.render = function(data) {
  return `<ul>
    ${data.collections.all.map(post =>
      `<li><a href="${post.url}">${post.url}</a></li>`
    ).join("\n")}
  </ul>`;
};How to Exclude content from Collections Jump to heading
In front matter (or further upstream in the data cascade), set the eleventyExcludeFromCollections option to true to opt out of specific pieces of content added to all collections (including collections.all, collections set using tags, or collections added from the Configuration API in your config file). Useful for your RSS feed, sitemap.xml, custom templated .htaccess files, et cetera.
---
eleventyExcludeFromCollections: true
tags: post
---
This will not be available in `collections.all` or `collections.post`.Add to a Collection using Tags Jump to heading
You can use a single tag, as in the above example OR you can use any number of tags for the content, using YAML syntax for a list.
A single tag: cat Jump to heading
---
tags: cat
---This content would show up in the template data inside of collections.cat.
Using multiple words in a single tag Jump to heading
---
tags: cat and dog
---If you use multiple words for one tag you can access the content by the following syntax collections['cat and dog'].
Multiple tags, single line Jump to heading
---
tags: ['cat', 'dog']
---This content would show up in the template data inside of collections.cat and collections.dog.
Multiple tags, multiple lines Jump to heading
---
tags:
  - cat
  - dog
---This content would show up in the template data inside of collections.cat and collections.dog.
Collection Item Data Structure Jump to heading
Always prefer…
<ul>
{%- for post in collections.post -%}
  <li>{{ post.data.title }}</li>
{%- endfor -%}
</ul><ul>
{%- for post in collections.post -%}
  <li>{{ post.data.title }}</li>
{%- endfor -%}
</ul>exports.render = function(data) {
  return `<ul>
    ${data.collections.post.map(post => `<li>${post.data.title}</li>`).join("\n")}
  </ul>`;
};Note in the above example that we output the post.data.title value? Similarly, each collection item will have the following data:
- inputPath: the full path to the source input file (including the path to the input directory)
- fileSlug: Mapped from the input file name, useful for permalinks. Read more about- fileSlug.
- outputPath: the full path to the output file to be written for this content
- url: url used to link to this piece of content.
- date: the resolved JS Date Object used for sorting. Read more about Content Dates.
- data: all data for this piece of content (includes any data inherited from layouts)
- templateContent: the rendered content of this template. This does not include layout wrappers.
{ inputPath: './test1.md',
  fileSlug: 'test1',
  outputPath: './_site/test1/index.html',
  url: '/test1/',
  date: new Date(),
  data: { title: 'Test Title', tags: ['tag1', 'tag2'], date: 'Last Modified' },
  templateContent: '<h1>This is my title</h1>\n\n<p>This is content…' }Sorting Jump to heading
The default collection sorting algorithm sorts in ascending order using:
- The input file’s Created Date (you can override using datein front matter, as shown below)
- Files created at the exact same time are tie-broken using the input file’s full path including filename
For example, assume I only write blog posts on New Years Day:
posts/postA.md (created on 2008-01-01)
posts/postB.md (created on 2008-01-01)
posts/post3.md (created on 2007-01-01)
another-posts/post1.md (created on 2011-01-01)
This collection would be sorted like this:
- posts/post3.md
- posts/postA.md
- posts/postB.md
- another-posts/post1.md
Sort descending Jump to heading
To sort descending in your template, you can use a filter to reverse the sort order. For example, it might look like this:
Always prefer…
<ul>
{%- for post in collections.post reversed -%}
  <li>{{ post.data.title }}</li>
{%- endfor -%}
</ul><ul>
{%- for post in collections.post | reverse -%}
  <li>{{ post.data.title }}</li>
{%- endfor -%}
</ul>exports.render = function(data) {
  let posts = data.collections.post.reverse();
  return `<ul>
    ${posts.map(post => `<li>${post.data.title}</li>`).join("\n")}
  </ul>`;
};You should not use Array reverse() on collection arrays in your templates, like so:
{%- for post in collections.post.reverse() -%}
This applies any time you use `reverse`, for example in a custom shortcode:
eleventyConfig.addShortcode("myShortcode", function (aCollection){
  // WARNING
  aCollection.reverse();
})This will mutate the array and re-order it in-place and will have side effects for any use of that collection in other templates.
Instead, use one of the many template engine utilities provided for you to do this, such as Liquid’s reverse or Nunjucks’ reverse
This is a Common Pitfall.
Overriding Content Dates Jump to heading
You can modify how a piece of content is sorted in a collection by changing its default date. Read more at Content Dates.
---
date: 2016-01-01
---Advanced: Custom Filtering and Sorting Jump to heading
To get fancier with your collections (and even do a bit of your own custom filtering, if you’d like), you can use our Configuration API.
Inside of your .eleventy.js config file, use the first argument to the config function (eleventyConfig below) to call the API:
module.exports = function(eleventyConfig) {
  eleventyConfig.addCollection("myCollectionName", function(collectionApi) {
    // get unsorted items
    return collectionApi.getAll();
  });
};addCollection can accept async functions too. Use await in your callback to do some asynchronous things!
Return values Jump to heading
- addCollectioncallbacks can return any arbitrary object type and it’ll be available as data in the template. Arrays, strings, objects—have fun with it.
Collection API Methods Jump to heading
The data collection gets passed to the callback. You can use it in all sorts of ways:
getAll() Jump to heading
Returns an array.
module.exports = function(eleventyConfig) {
  // Unsorted items (in whatever order they were added)
  eleventyConfig.addCollection("allMyContent", function(collectionApi) {
    return collectionApi.getAll();
  });
};module.exports = function(eleventyConfig) {
  // Filter using `Array.filter`
  eleventyConfig.addCollection("keyMustExistInData", function(collectionApi) {
    return collectionApi.getAll().filter(function(item) {
      // Side-step tags and do your own filtering
      return "myCustomDataKey" in item.data;
    });
  });
};module.exports = function(eleventyConfig) {
  // Sort with `Array.sort`
  eleventyConfig.addCollection("myCustomSort", function(collectionApi) {
    return collectionApi.getAll().sort(function(a, b) {
      //return a.date - b.date; // sort by date - ascending
      return b.date - a.date; // sort by date - descending
      //return a.inputPath.localeCompare(b.inputPath); // sort by path - ascending
      //return b.inputPath.localeCompare(a.inputPath); // sort by path - descending
    });
  });
};Curious where the date is coming from? Read more about Content Dates.
Note that the last example adding the myCustomSort collection will be available in your templates as collections.myCustomSort.
getAllSorted() Jump to heading
Returns an array.
module.exports = function(eleventyConfig) {
  // Use the default sorting algorithm (ascending by date, filename tiebreaker)
  eleventyConfig.addCollection("allMySortedContent", function(collectionApi) {
    return collectionApi.getAllSorted();
  });
};module.exports = function(eleventyConfig) {
  // Use the default sorting algorithm in reverse (descending dir, date, filename)
  // Note that using a template engine’s `reverse` filter might be easier here
  eleventyConfig.addCollection("myPostsReverse", function(collectionApi) {
    return collectionApi.getAllSorted().reverse();
  });
};Note that while Array .reverse() mutates the array in-place, all Eleventy Collection API methods return new copies of collection arrays and can be modified without side effects to other collections. However, you do need to be careful ⚠️ when using Array .reverse() in templates!
module.exports = function(eleventyConfig) {
  // Filter using `Array.filter`
  eleventyConfig.addCollection("onlyMarkdown", function(collectionApi) {
    return collectionApi.getAllSorted().filter(function(item) {
      // Only return content that was originally a markdown file
      let extension = item.inputPath.split('.').pop();
      return extension === "md";
    });
  });
};getFilteredByTag( tagName ) Jump to heading
Returns an array.
module.exports = function(eleventyConfig) {
  // Get only content that matches a tag
  eleventyConfig.addCollection("myPosts", function(collectionApi) {
    return collectionApi.getFilteredByTag("post");
  });
};getFilteredByTags( tagName, secondTagName, […] ) Jump to heading
Retrieve content that includes all of the tags passed in. Returns an array.
module.exports = function(eleventyConfig) {
  // Get only content that matches a tag
  eleventyConfig.addCollection("myTravelPostsWithPhotos", function(collectionApi) {
    return collectionApi.getFilteredByTags("post", "travel", "photo");
  });
};getFilteredByGlob( glob ) Jump to heading
Returns an array. Will match an arbitrary glob (or an array of globs) against the input file’s full inputPath (including the input directory).
getFilteredByGlob filters results returned from getAllSorted. It will not search the file system for new templates. It will not match files in your _includes directory or anything excluded by eleventyExcludeFromCollections.
getFilteredByGlob will not "find" files that are not supported by Eleventy. For example, a file with the extension .ray will be ignored even if it would match the glob.
module.exports = function(eleventyConfig) {
  // Filter source file names using a glob
  eleventyConfig.addCollection("onlyMarkdown", function(collectionApi) {
    return collectionApi.getFilteredByGlob("**/*.md");
  });
};module.exports = function(eleventyConfig) {
  // Filter source file names using a glob
  eleventyConfig.addCollection("posts", function(collectionApi) {
    return collectionApi.getFilteredByGlob("_posts/*.md");
  });
};module.exports = function(eleventyConfig) {
  // Filter source file names using a glob
  eleventyConfig.addCollection("posts", function(collectionApi) {
    // Also accepts an array of globs!
    return collectionApi.getFilteredByGlob(["posts/*.md", "notes/*.md"]);
  });
};