How to automatically generate categories pages in Jekyll

If you take a look at my website and click on a category (for example this one is categorized as jekyll) it will take you to a new page /categories/jekyll/ and you will be able to see all the ‘jekyll’ posts.

This is actually quite simple to set up. I’ll show you step by step how to use a plugin so that a page like /category/:category (where :category is the chosen category) gets automatically generated.

Step 1

Create a plugin directory in the root of your project and name it _plugins.

Step 2

Inside this directory create a file and name it categories.rb. Copy and paste the following code into the file. (The code comes straight from Jekyll documentation on generators)

module Jekyll
  class CategoryPageGenerator < Generator
    safe true

    def generate(site)
      if site.layouts.key? 'category_index'
        dir = site.config['category_dir'] || 'categories'
        site.categories.each_key do |category|
          site.pages << CategoryPage.new(site, site.source, File.join(dir, category), category)
        end
      end
    end
  end

  # A Page subclass used in the `CategoryPageGenerator`
  class CategoryPage < Page
    def initialize(site, base, dir, category)
      @site = site
      @base = base
      @dir  = dir
      @name = 'index.html'

      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'category_index.html')
      self.data['category'] = category

      category_title_prefix = site.config['category_title_prefix'] || 'Category: '
      self.data['title'] = "#{category_title_prefix}#{category}"
    end
  end
end

The above is what is called a generator. Generators need to implement the generate method. This generator is what is going to allow us to have a page for each of our categories without us having to manually create this.

Step 3

Now we need a layout for our categories pages. Inside your _layouts folder create one called category_index.html. You can create this layout yourself to fit your site design but the following is an example of mine

---
layout: default
---

<section class="small-intro">
  <div class="container">
    <h2>
      {{page.category | capitalize}}
    </h2>
  </div>
</section>
<section class="blog">
  <div class="container">
    {% for post in site.posts %} {% if post.categories contains page.category %}

    <div class="blog-container">
      <div class="title-excerpt">
        <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
      </div>
      <div class="date-categories">
        <div class="categories">
          {% for category in post.categories%}
          <div class="category">
            <a href="/categories/{{category}}">{{ category }}</a>
          </div>
          {% endfor %}
        </div>
        <div class="date">
          <p>{{ post.date | date_to_string }}</p>
        </div>
      </div>
    </div>
    {% endif %} {% endfor %}
  </div>
</section>

As you can see we are getting the page.category which is the category you clicked on or are searching for. Then we loop through our posts and if the post category matches the category in the page we will display the post.

That’s it! To summarize, the generator plugin from step 2 will run after Jekyll knows all the categories for the site but before the site is generated.

Don’t forget to update the links in the places you are cycling through your categories to link to

<a href="/categories/{{category}}">{{ category }}</a>

Bonus

We can also make a /category like I have here. To do this create a categories directory in the root directory. Inside create an index.html page.

Aside from just listing each category I am also listing the count for each category.

Your /category/index.html should look like this:

---
layout: default
title: Ed Ruiz - Categories
---

<section class="small-intro">
  <div class="container">
    <h2>
      All categories
    </h2>
  </div>
</section>
<section class="blog">
  <div id="categories">
    <div class="container">
      {% for cat in site.categories %}
      <div class="category">
        {% for inner in cat%} {% if forloop.first == true %}
        <span class="span-cat">
          <a href="/categories/{{cat[0]}}">{{cat[0]}}</a></span
        >
        -
        <span class="span-count">
          <a href="/categories/{{cat[0]}}"
            >{{site.categories[inner].size}}</a
          ></span
        >
        {% endif %} {% endfor %}
      </div>
      {% endfor %}
    </div>
  </div>
</section>

Now if you head over to /category you should see all the categories and the amount of posts.

I hope this helps you set up the dynamic categories and category pages like I have set up on my site.