Kyle Banks

Creating Category Pages in Jekyll without Plugins

Written by @kylewbanks on Dec 10, 2016.

Recently I added categories to all the posts on this blog so that I could link to a page containing posts for a single category. For instance, a post could be categorized as Android or Unity3D, and visitors could see all posts related to that particular category. Additionally, it allows me to add Related Posts links to each post as you can see on the right.

When I set about to implement this, I had assumed Jekyll would have this functionality built in, considering they do provide pre-defined category front-matter, but unfortunately this was not the case. It turns out most people end up using plugins to achieve this, but I figured I could make it work without the use of plugins, and today I’ll be showing you how I did just that.

Category Collection Definition

The first thing to do is to define the category collection in your config.yml. This allows you to use the collection throughout your templates, which we’ll need later on. Simply define your collection like so in your config.yml:

collections:
  category:
    output: true

Note the output: true. This allows us to generate pages for each category where we can list all posts under that category. For our convenience, we’ll add a default layout property to each category in the config.yml as well:

collections:
  category:
    output: true

defaults:
  -
    scope:
      path: ""
      type: category
    values:
      layout: "category"

This sets the default layout to category for all elements of our category collection, and we’ll be using this right away.

Layout

Next we’ll create a category layout in the _layouts directory. This layout is going to be used to create the category list page, so the contents will vary depending on your site, but for me it looks like so:


---
layout: default
---

<div class="blog list">
    <h1>Filed Under <small>#{{ page.tag }}</small></h1>

    {% for post in site.categories[page.tag] %}
        {% include post_preview.html %}
    {% endfor %}
</div>

This layout is essentially finding all posts with the current category (page.tag) and rendering a preview of each. page.tag will contain the name of the category as defined in our category collection, which is the next thing for us to do.

Categories

The one draw-back to this technique, is we’re actually going to have to create a file for each category we want to use. In your root directory, create a _category directory, and place a file in the directory for each category you want to support. For instance, I have an android.md, golang.md, java.md, etc. Each file contains only two lines of front-matter that we’ll be using to render our collection pages, and to link to them.

For example, my android.md file contains the following:

---
tag: android
permalink: "/category/android"
---

Once you’ve created all the category collection files, you’re ready to start tagging your posts.

Categorizing Posts

Now you’re ready to categorize posts, which is done via front-matter in each of your posts. I wanted to make sure that I could have multiple categories for a single post when necessary, and you can do that by using the categories front-matter property like so:

---
title: "Debugging Docker Containers on Elastic Beanstalk"
preview: "While deploying Docker containers on Amazon's Elastic Beanstalk is about as easy as it gets, it can be a little tricky to debug the container. For example, you may want to tail the logs, or open a shell inside the container to see what's going on." 
permalink: "/blog/debugging-docker-containers-on-elastic-beanstalk" 
layout: "post" 

categories: 
    - "architecture"
    - "aws"
---

... post contents ...

Notice the categories array at the bottom of of the front-matter, where the post is categorized as architecture and as aws.

Once your posts are categorized, you should be able to run your site and go to /category/<category name> and see each of the posts under that category!

One of the primary reasons I wanted to implement categories was to display related posts for each blog post on the site. The idea is to display the most recent posts under the same category, without displaying the post currently being read.

In my post layout, I added the following:


<div class="related">
    <h3>Related Posts</h3>
    {% assign firstCategory = page.categories | first %}
    {% assign relatedCount = 0 %}
    {% for related in site.categories[firstCategory] %}
        {% unless page.permalink == related.permalink %}
            {% assign relatedCount = relatedCount | plus: 1 %}
            <a href="{{related.permalink}}">{{ related.title }}</a>
        {% endunless %}

        {% if relatedCount == 3 %}
            {% break %}
        {% endif %}
    {% endfor %}
</div>

What this does is use the first category of the post (remember we can have multiple categories on a single post), and loops over all posts in the same category. If the post in the current iteration doesn’t have the same URL (permalink) as the post being displayed, a link to the post is displayed and the relatedCount integer is incremeted. Once we run out of posts, or three post are displayed, we break out of the loop.

Display Categories

Finally, the last thing I wanted to do was display the categories of the post in the post header, right beneath the title. This is simpler than the Related Posts links, because we just have to iterate over each category in the post and display a link.


<h1>{{ page.title }}</h1>
<div class="tags">
    {% assign sortedCategories = page.categories | sort %}
    {% for category in sortedCategories %}
        <span class="tag">
            <a href="/category/{{ category }}">#{{ category }}</a>
        </span>
    {% endfor %}
</div>

Right below the title of the post, I sort the categories so they appear in alphabetical order (just a personal preference). Then, for each category in the sorted category list, I simply create a link to that categories page.

And that’s it, no plugins required. It is a bit cumbersome to have to create a file for each of your categories in the _category directory, however they’re just small two-liner files so I’m okay with that for now. To me this has been a better solution that integrating a plugin, as I tend to avoid them whenever possible. Once your posts are categorized, you can come up with many uses for the categories, so let me know down below if you make anything cool!

Let me know if this post was helpful on Twitter @kylewbanks or down below!