Jekyll is a popular blog engine that allows you to create static websites with markdown files. One of the features that Jekyll offers is the ability to tag your posts with keywords that describe their content. Tags are useful for a blog because they help you organize your posts, make them easier to find, and improve your SEO. In this post, I will show you how to use tags in Jekyll and how to create a tags page and a tags feed for your blog.

Jekyll has support for tags and categories, however using the Tag functionality comes with certain gotcha’s if you want to maintain Github Pages compatibility.

The goals are

  • smoother discovery of related content
  • a visual overview of top tags
  • The ability to add some extra content to my tags pages.

This is the first post in a series on Tags in Jekyll, check Tags in Jekyll: Word Cloud and Tags in Jekyll: related posts.

Defining Tags

At the top of your posts page, you can definte them. The tags for this post are defined in the front matter:

1
2
3
4
5
6
7
8
---
layout: post
title: "Tags in Jekyll: the setup"
tags: 
 - Web Development
 - Jekyll
---

Creating Tag pages

There are 2 main approaches, one using the pattex/jekyll-tagging plugin. The other one using a collections. I recommend on reading Jekyll Collections. The second approach has the advantage of being Jekyll native.

In _config.yml create a new collection post_tags and some defaults, and a corresponding _post_tags folder.

1
2
3
4
5
6
7
8
9
10
11
12
collections:
  post_tags:
    permalink: /tags/:name/
    output: true

defaults:
  - scope:
      path: ''
      type: post_tags
    values:
      permalink: "/tags/:name/"
      layout: tag

The disadvantage of this model, is having to create a file for each tag in the _post_tags folder. To know which ones are missing I have this small _site_tags_missing.txt file I generate. It will loop over all the found site.tags and see if there is a post_tag with the corresponding title (which also means my tag name can be different then the url).

Content for a tag page

1
2
3
4
5
6
7
8
9
10
11
12
---
permalink: /_site_tags_missing.txt
---
{% if jekyll.environment != 'production' %}
{%- assign post_tags = site.post_tags | map: "title" -%}
{%- for tag in site.tags -%}
    {%- if post_tags contains tag[0] -%}
    {% else %}
{{ tag[0] }}
    {%- endif -%}
{%- endfor -%}
{% endif %}    

Inside the _post_tags folder I have a series of files, one for each tag. The filename will become the /tags/FILENAME url, and the title attribute the name. This allows the #Web Development tag to become /tags/webdev/. Any content is rendered on the tag page.

1
2
3
4
---
title: Home Assistant
---
[Home Assistant](https://www.home-assistant.io/) is a versatile and open source platform that lets you control your home devices and services. It works great with [#Zigbee]({% include tag_url tag="Zigbee" %}) on [#NixOS]({% include tag_url tag="NixOS" %}).

A layout for the tag pages

Let’s start styling the layout of the individual tag page. I want to give an overview of all the posts tagged. I created a custom tag.html layout in the _layouts folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
---
layout: base
---
<article class="post">
    <header class="post-header">
        <h1 class="post-title">{{ page.title | escape }}</h1>
    </header>

    <div class="post-content">
        {{ content }}
        {% assign tag = page.title | sluggify %}
        {% assign tag_posts = site.tags[tag] %}
        <ul class="post-list">
            {% for post in tag_posts %}
            <li>
                {% assign date_format = site.minima.date_format | default: "%b %-d, %Y" %}
                <span class="post-meta">
                    {{ post.date | date: date_format }}
                    {%- if post.tags -%}
                    <span class="tags">
                        {% for tag in post.tags %}            
                            {% if tag == page.title %}
                            {% else %}
                            <a href="{% include tag_url tag=tag %}">#{{ tag }}</a> 
                            {% endif %}
                        {% endfor %}
                      </span>
                    {%- endif -%}
                </span>

                <h4>
                    <a href="{{ post.url | relative_url }}">{{ post.title | escape }}</a>
                </h4>
            </li>
            {% endfor %}
        </ul>

        Browse more <a href="/tags">tags</a>.


    </div>
</article>

An example of this can be viewed at /tags/ai. In the posts overview I also want to highlight the other tags for the particular post, this is accomplished line 22-26. I’m using a little helper include, _includes/tag_url to make sure I link to an existing post_tag (and fallback to /tags if not).

1
2
3
4
5
6
7
8
{%- capture url -%}
    {% for pt in site.post_tags %}
        {% if pt.title == include.tag %}
            {{ pt.url | relative_url }}
        {% endif %}
    {% endfor %}
{%- endcapture -%}
{{ url | strip | default: "/tags/" | relative_url}}

Linking to the tags

Before (or after) the title, as part of the post-meta I added following code.

1
2
3
4
5
6
7
8
9
10
11
{% assign date_format = site.minima.date_format | default: "%b %-d, %Y" %}
<span class="post-meta">
  <span class="date">{{ post.date | date: date_format }}</span>
  {%- if post.tags -%}
  <span class="tags">
    {% for tag in post.tags %}            
      <a href="{% include tag_url tag=tag %}">#{{ tag }}</a>
    {% endfor %}
    </span>
  {%- endif -%}
</span>

Conclusion

Having a pure Jekyll & liquid version is possible, but takes some efforts and head scratching. Especially how liquid filters work. I recommend you to read the Liquid Docs before starting. This blog is fully on GitHub, so take a peak.