Jekyll: Generate pages for each tags

Jekyll: Generate pages for each tags

As I am in the process of converting some of the WordPress based blogs to to use the static website builder Jekyll. This will allow instant response to the users without loading my self hosting servers. Also it is easier to server the blog under same domain without conditional routing of traffic between different docker services as all the content can be served using an nginx server. One

While converting the static website from WordPress, one of the things I wanted is to have is dedicated pages specific to each categories and tags. Here is the step by step instructions for anyone want to implement the similar features in your Jekyll static website.

We are going to use the Jekyll Generators for this purpose. Having understanding in of Ruby programing language will help but can having general understanding of any language as all follow same pattern.

We will create a Plugin for this purpose. If the plugin reside inside one file we can directly use it otherwise we will have to compile them into Gem and use it. We will make this simple. First create _plugins folder inside the root directory of your Jekyll project. Inside the directory create a file named Tags.rb Please note that the extension should be .rb and the file name can be anything.

Inside the Tags.rb copy and paste below code. Here we create a TagPageGenerator class by inhering Jekyll Generator. Inside that we override the generate method and create instances of TagPage class and add them to the pages list of the sites. TagPage class is defined below and inherits Jekyll Page class.

class TagPageGenerator < Jekyll::Generator
      safe true
  
      def generate(site)
        site.tags.each do |tag, posts|
          tag_name = tag.downcase
          site.pages << TagPage.new(site, tag_name , posts)
        end
      end
    end
  
    # Subclass of `Jekyll::Page` with custom method definitions.
    class TagPage < Jekyll::Page
      def initialize(site, tag, posts)
        @site = site             # the current site instance.
        @base = site.source      # path to the source directory.
        @dir  = tag         # the directory the page will reside in.
  
        # All pages have the same filename, so define attributes straight away.
        @basename = 'index'      # filename without the extension.
        @ext      = '.html'      # the extension.
        @name     = 'index.html' # basically @basename + @ext.
  
        # Initialize data hash with a key pointing to all posts under current tag.
        # This allows accessing the list in a template via `page.linked_docs`.
        @data = {
          'title' => "Tags - #{tag}",
          'posts' => posts,
          'tag' =>  tag
        }
  
        # Look up front matter defaults scoped to type `tags`, if given key
        # doesn't exist in the `data` hash.
        data.default_proc = proc do |_, key|
          site.frontmatter_defaults.find(relative_path, :tags, key)
        end
      end
  
      # Placeholders that are used in constructing page URL.
      def url_placeholders
        {
          :path       => @dir,
          :tag   => @dir,
          :basename   => basename,
          :output_ext => output_ext,
        }
      end
    end
  end

Inside the TagPage class we define the data as shown below.

@data = {
          'title' => "Tags - #{tag}",
          'posts' => posts,
          'tag' =>  tag
        }

These details are passed to the Tag pages created and we can refer them in the usual way as page variable. For example title can be referred as {page.title} inside the template.

Also there is a code as shown below to set defaults for the pages.

        # Look up front matter defaults scoped to type `tags`, if given key
        # doesn't exist in the `data` hash.
        data.default_proc = proc do |_, key|
          site.frontmatter_defaults.find(relative_path, :tags, key)
        end

This allows us to set the default layout pages for the Tags pages inside the _config.yml file as shown below.

defaults:
  - scope:
      type: tags # select all tags pages
    values:
      layout: tag_page
      permalink: tags/:tag/

Now we have create created the plugin and configured the default layout in _config.yml file, and needs to create the layout and fill the content. Below is the sample code for the layout.

---
layout: default
---

<h1 class="text-start">Posts with tag: {{ page.tag }}</h1>
        <ul>
          {% for post in page.posts %}
          <li>
            <a href="{{ post.url }}">{{ post.title }}</a> ({{ post.date |
            date_to_string }} by {{ post.author }})
          </li>
          {% endfor %}
        </ul>

You can adjust the layout and formatting as you require. Please note that the values passed from the our TagGenerator plugin is referred inside the template.

Now exec the Jekyll serve command as shown below.

bundle exec jekyll serve

You should see under your _sites directory and tag directory is generated and inside directories are generated for each tags. For this to work you should have tags added inside the posts in front matter. Below is the sample Tag page you should see for each tags.

Hope you had learned something. I am sure you will read this blog also in Jekyll soon as I am planning to convert this as well soon. Happy learning and you can ask your questions here and I am happy to answer.

A full stack developer learning a developing web applications since my university time for more than 20 years now and Pega Certified Lead System Architect (since 2013) with nearly 16 years experience in Pega.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top