Creating a page for each tag in Jekyll
Jekyll lets you assign tags to posts, but it does not automatically create a browsable page for each tag. A small generator plugin can fill that gap.
Start by adding tags to a post’s front matter. I prefer a YAML array because it stays unambiguous when a tag contains spaces:
layout: post
title: Tags in Jekyll
description: Adding tags to a Jekyll powered website
summary: Adding tags to a Jekyll powered website.
tags: [tags, jekyll]
Next, create _plugins/tags.rb. The generator collects every unique tag and adds one page under /tag/<tag>/:
module Jekyll
class TagPageGenerator < Generator
safe true
def generate(site)
tags = site.posts.docs.flat_map { |post| post.data['tags'] || [] }.to_set
tags.each do |tag|
site.pages << TagPage.new(site, site.source, tag)
end
end
end
class TagPage < Page
def initialize(site, base, tag)
@site = site
@base = base
@dir = File.join('tag', tag)
@name = 'index.html'
self.process(@name)
self.read_yaml(File.join(base, '_layouts'), 'tag.html')
self.data['tag'] = tag
self.data['title'] = "Tag: #{tag}"
end
end
end
Each generated page reads its markup from _layouts/tag.html. Add this layout:
---
layout: default
---
<h1>{{page.tag}}</h1>
<ul>
{% for post in site.posts %}
{% if post.tags contains page.tag %}
<li><a class="post" href="{{ post.url }}">{{ post.title }}</a></li>
{% endif %}
{% endfor %}
</ul>
The page loops through the site’s posts and lists only those containing the current tag. To make those pages discoverable, add tag links to the post layout:
<p>
Tagged
{% for tag in page.tags %}
<a class="post" href="/tag/{{tag}}">#{{tag}}</a>{% unless forloop.last %}, {% endunless %}
{% endfor %}
</p>
This approach uses a custom plugin, so it will not run in environments that build Jekyll in safe mode without your plugin. If the hosting platform blocks custom plugins, generate the tag pages before deployment or create them explicitly. Otherwise, this keeps the tag archive in sync without maintaining a page by hand for every new label.