Taxonomies in Gutenber 0.4

I finally have categories in my sidebar!

This comes from the move to Gutenberg 0.4, with its new taxonomies setup. Getting it migrate took a bit figuring out so I thought I would document it here.

Unlike earlier Gutenberg, which had the predefined taxonomies category and tags, Gutenberg 0.4 allows you to define whatever taxonomies you want. The hierarchy goes like this: At the top you have a list of taxonomies. Each taxonomy consists of a name and a few other options. categories would be a taxonomy with the name categories. Then each taxonomy itself has a list of taxonomy items. Like the categories taxonomy can contain the taxonomy items Webdev and Books.

To setup taxonomies in 0.4 the first step was to define taxonomies in config.toml file. The only taxonomy I used was categories, so I defined it like this:

[[taxonomies]]
name = "categories"
rss = false

Once I had my one taxonomy, categories, defined I needed to update the front matter on all my pages. Where before there was just a category key you could set, there is now a taxonomies section. On this post it looks like this:

[taxonomies]
categories = ["Webdev"]

Categories is now also a list because you can assign multiple taxonomy items in the same taxonomy to a page, like tags used to work. Mappings between a taxonomy items and pages is always many-to-many. You do not need to define taxonomy items, Gutenberg creates the for you based on when you assign them to pages.

The next step was to update the page.html template for the new taxonomies. Where page used to have a category key, it now has a taxonomies key, which is a HashMap from the name of the taxonomy to a list of the names of the assigned taxonomy items. So on this post it looks something like this (expressed in JSON):

"page": {
  "taxonomies": {
    "categories": [
      "Webdev"
    ]
  }
}

You can still use the get_taxonomy_url to get a url to the page for a specific taxonomy item. It takes two parameters: kind, which is the name of the taxonomy; and name, which is the name of the taxonomy item. So you get a link to the page for the Webdev taxonomy with: get_taxonomy_url(kind="categories", name="Webdev").

I updated the post header to this:

<header>
  <h2>{{page.title}}</h2>
  <div class="post-info">
    Date: {{page.date}}
    {% if page.taxonomies.categories %}
      | Category: <a href="{{get_taxonomy_url(kind="categories", name=page.taxonomies.categories[0])|safe}}">{{page.taxonomies.categories[0]}}</a>
    {% endif %}
  </div>
</header>

I check if there are any categories assigned to the post, and if there are I show the first one as a link to the page for that category. This design means I can only show one category per post, but I figure it would do for now.

The old hard coded category.html template is gone in 0.4, so the next step was to make templates for the categories taxonomy. Each taxonomy gets two templates now, in a folder named after the taxonomy. The two templates are:

  1. list.html. This is the template for the entire taxonomy, with access to a list of taxonomy items. You can see how it looks like for my categories taxonomy here.
  2. single.html. This is the template for a single taxonomy item, like Webdev. You can see what it looks like for Webdev here. It is also what the Webdev at the top of this post links to.

The list.html template get passed in a global variable called terms, which is a list of taxonomy items. Each item consists of the name of the item, the slug for the item, the permalink for the item and pages, a list of pages that has that taxonomy item assigned. My list.html for categories is quite simple. The main component are two for loops that loop through each taxonomy item in the taxonomy and then through each page in the taxonomy item:

<ul class="categories-list">
  {% for term in terms %}
    <li>
      <a href="{{term.permalink}}">{{term.name}}</a>
      <ul>
        {% for page in term.pages %}
          <li>
            <a href="{{page.permalink}}">{{page.title}}</a> - {{page.date}}
          </li>
        {% endfor %}
      </ul>
    </li>
  {% endfor %}
</ul>

The single.html templates gets passed a term variable, which is the taxonomy item being rendered. My template is basically just replicating my index.html template but now it loops through the pages in the taxonomy item.

Finally, I got to the reason for this entire migration: a list of categories in the sidebar. Gutenberg 0.4 adds another function to templates: get_taxonomy. It takes one argument, kind, which is the name of the taxonomy. It returns a Taxonomy object with two fields: kind, with details about the taxonomy from the config.toml file; and items, a list of taxonomy items. With this function you can access the details of a taxonomy anywhere. I updated my sidebar template like so:

{% set categories = get_taxonomy(kind="categories") %}
<h2>Categories</h2>
<ul>
  {% for category in categories.items %}
    <li>
      <a href="{{category.permalink}}">{{category.name}}</a> ({{category.pages|length}})
    </li>
  {% endfor %}
</ul>

And so, at long last, a list of categories in the sidebar!