Mark Llobrera

Eleventy: Custom Content Type and Collection for Books

Continuing my adventures in Eleventy: I wanted to create a collection for books that I’ve read, separate from the default Post type. But I didn’t want to have the collection called Books, because that would imply that I had written the books.

I settled on a compromise: I would call my custom type Book, but my collection would be called Reading.

Book Type

Creating a type called book was fairly simple:

reading.json is very concise:

"layout": "book",
"content_type": "book"

This specifies two things:

This means that for my collection I can grab everything of content type book, and that each book file will be rendered by the book.njk template file.

A book’s front matter has metadata (this came out of my recent project for tracking my reading):

title: Gideon the Ninth
author: Tamsyn Muir
date: 2020-05-21
description: Tamsyn Muir’s galactic necromancy saga.
publication_date: 2019-09-10
cover_image: reading/gideon-the-ninth.jpg
genre: Fiction
format: Hardcover
reading_start_date: 2020-05-16
reading_end_date: 2020-05-16
- reading

Everything after that Markdown, just like you would find in a default post.

Retrieving the Collection

With the Book content type defined, I can now retrieve my collection in my .eleventy.js file. Originally I did this:

eleventyConfig.addCollection("reading", function (collection) {
return collection.getAll().filter(function (item) {
return == "book";

Which worked — I got all my books back in a list. Except … the order of books was unstable. Even with a date set in the YAML front matter for each book post, the order of the books kept changing depending on which was saved last. So I tacked on a sort() call, using this example from the docs:

eleventyConfig.addCollection("reading", function (collection) {
return collection
.filter(function (item) {
return == "book";
.sort(function (a, b) {
return -;

Now the order is explicitly dependent on the date from the front matter. This also means I don’t really have to use the reverse filter within my books list template.


Books Shell Template

Now that I have my collection, I can render out a list for my “Reading” page. I created a books.njk template, which looks like this:

layout: layouts/home.njk
permalink: /reading/
key: Reading
order: 2
<div class="content-main container">

{% set bookslist = collections.reading %}
{% include "bookslist.njk" %}

That creates a “Reading” link in the main navigation, and maps /reading as the URL. It also sets a variable, bookslist, that is the result of the collection I retrieved.

Books List Template

bookslist.njk looks very much like postslist.njk — note that I’m not reversing the order of my bookslist, because the collection is already sorted reverse chronologically. Note the book-specific[X] front matter variables:

<ul reversed class="postlist">
{% for book in bookslist %}
<li class="postlist-item{% if post.url == url %} postlist-item-active{% endif %}">
<a href="
{{ book.url | url }}" class="postlist-link">{% if %}{{ }}{% else %}<code>{{ book.url }}</code>{% endif %}</a>
{{ }}</p>
<span class="post-meta">Finished <time class="postlist-date" datetime="
{{ | htmlDateString }}">{{ | readableDate }}</time></span>

{% endfor %}

Book Detail Template

Finally, book.njk, which renders a single book post. Nothing fancy here, just printing out a bunch of front matter variables and sanitized Markdown content:

layout: layouts/base.njk
templateClass: tmpl-book
<article class="content-main container book-single">
<div class="book-details">
<div class="book-cover book-shadow">

{% figure cover_image, "", "book-thumb" %}
<div class="book-meta">
{{ title }}</h1>
<dt>Publication Date</dt>
{{ publication_date | readableDate }}</dd>
{{ genre }}</dd>
{{ format }}</dd>
<dt>Started Reading</dt>
{{ reading_start_date | readableDate }}</dd>
<dt>Finished Reading</dt>
{{ reading_end_date | readableDate }}</dd>
<div class="book-body">

{{ content | safe }}
<p><a href="
{{ '/reading' | url }}">← All Books</a></p>

What’s Still Confusing

So I’ve got a custom type now, which feels good — I can just treat books as a first-class type instead of using a tag to filter out posts. For the life of me I can’t figure out how to get a custom taxonomy working, however. It’s not quite necessary for what I want to build, but that is usually a default thing I have to consider when I’m in WordPress/Drupal-land.