49

I am using the standard jekyll installation to maintain a blog, everything is going fine. Except I would really like to tag my posts.

I can tag a post using the YAML front matter, but how do I generate pages for each tag that can will list all posts for a tag?

Alan W. Smith
  • 24,647
  • 4
  • 70
  • 96
raheel
  • 1,407
  • 1
  • 15
  • 24

8 Answers8

81

Here is a solution with alphabetically sorted tags on a single page.
It uses Liquid only, which means that it works on GitHub Pages:

{% capture tags %}
  {% for tag in site.tags %}
    {{ tag[0] }}
  {% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}

{% for tag in sortedtags %}
  <h3 id="{{ tag }}">{{ tag }}</h3>
  <ul>
  {% for post in site.tags[tag] %}
    <li><a href="{{ post.url }}">{{ post.title }}</a></li>
  {% endfor %}
  </ul>
{% endfor %}

You can see it in action here.


EDIT:

There's also a way to generate a separate page for each tag without plugins (which will work on GitHub Pages).

I have a more detailed explanation on my blog:
Separate pages per tag/category with Jekyll (without plugins)

First, you need a new layout file:

/_layouts/tagpage.html:

---
layout: default
---

<h1>{{ page.tag }}</h1>

<ul>
{% for post in site.tags[page.tag] %}
  <li>
    {{ post.date | date: "%B %d, %Y" }}: <a href="{{ post.url }}">{{ post.title }}</a>
  </li>
{% endfor %}
</ul>

With this layout file, you can add a new tag page by adding a new file with just two lines of YAML front-matter.

Here's an example for the jekyll tag:

/tags/jekyll/index.html:

---
layout: tagpage
tag: jekyll
---

The only disadvantage of this approach: each time you use a new tag for the first time, you have to remember to create a new two-line file for it.

To generate the root index file (i.e. the list of tags that links to /tags/jekyll/index.html etc.), you can use a similar solution like the one on top of this answer where I generate a single page with alphebetically sorted tags:

{% capture tags %}
  {% for tag in site.tags %}
    {{ tag[0] }}
  {% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
    <a href="/tags/{{ tag }}/">{{ tag }}</a><br>
{% endfor %}

This will generate a list of links like this:

<ul>
    <li><a href="/tags/.net/">.net</a></li>
    <li><a href="/tags/authentication/">authentication</a></li>
    <li><a href="/tags/backup/">backup</a></li>
</ul>

Note that this solution uses a blank to split tags, so it doesn't work when your tags contain blanks and Yevgeniy Brikman's comment applies here as well.

Community
  • 1
  • 1
Christian Specht
  • 35,843
  • 15
  • 128
  • 182
  • its like grabbing the information and then assigning it to a var, thanks for the post. i'm not sure what i'll do, maybe nothing. – John Apr 01 '14 at 15:06
  • 2
    This is a clever hack! One issue I had is that my tags had spaces in them, so the `split: ' '` would split one tag into many words. To work around it, I used a `|` character as a delimiter instead of spaces and split on that: https://gist.github.com/brikis98/e71d6c736158080968f5 – Yevgeniy Brikman Apr 16 '15 at 19:36
  • But then what does your index.html in root look like? Does it just list all of the tags/categories as links so when you click them it takes you to the tags/jekyll/index.html page? – Jwan622 Aug 03 '15 at 11:35
  • 2
    @Jwan622: I just edited the answer and added example code for how to do this. – Christian Specht Aug 10 '15 at 09:49
  • If you run into capitalization messing with the `sort`, you can use `sort_natural` instead. I also ran into the tags having newlines and whitespaces (even though they don't in their front matter) so I had to pass to strip_newlines and strip like so: `{% assign sortedtags = tags | strip_newlines | split: '|' | sort_natural %} {% for tag in sortedtags %} {% assign strippedtag = tag | strip %}

    {{ strippedtag }}

    `
    – SeanFromIT Sep 20 '18 at 00:30
  • I'm trying to wrap my head around creating my new website in Jekyll or Hugo and using Bootstrap 5 or not. Your answer above here I have tons like them and want to convert all my answers into blog posts on my new website. Extracting all my stack exchange answers accepted or with 2 or more votes is easy. Converting to blog post and which technology to use for website is not so easy. Do you have an answer posted here or a blog post about doing that? – WinEunuuchs2Unix Oct 21 '21 at 01:26
  • I've written a `pre-push` hook to [detect missing tag pages](https://moments.putnamhill.net/2023/02/22/jekyll-on-personal-site/) and a bash script, `gentag`, to make tag pages. Thanks for the solution! – Cole Tierney Feb 27 '23 at 21:56
14

This gist will generate a page per category for you: https://gist.github.com/524748

It uses a Jekyll Generator plugin, plus a Page subclass.

Alan W. Smith
  • 24,647
  • 4
  • 70
  • 96
Brian Clapper
  • 25,705
  • 7
  • 65
  • 65
8

Have a look at sites using jekyll. There are a few custom forks which have implemented tagging functionality, hopefully also in the way you want :-)

Bitterzoet
  • 2,752
  • 20
  • 14
5

I had the same question, and stumbled upon this: http://gist.github.com/143571.

It's a rake task which generates a tag list. I modified it slightly, and my version is at: http://github.com/mattfoster/mattfoster.github.com/blob/master/Rakefile.

Whilst this doesn't give you a page per tag, you can use anchors, which is half way there!

Matt
  • 5,522
  • 5
  • 29
  • 24
  • You can generate a list of tags without a Rake task, just using Liquid. Check out my [tags page](https://github.com/edrex/edrex.github.io/blob/master/pages/tags.html) for an example. – Eric Drechsel Sep 15 '13 at 18:40
1

I use the great Jekyll Tagging plugin that automatically generates a tags cloud and tag pages. Easy to install and use.

Here is a page for the "photo" tag on my blog (in french), and you can see the tags cloud in the bottom.

Nicolas Hoizey
  • 1,954
  • 1
  • 17
  • 24
1

Based on Christian's answer above I made a bash script that does what he described.

https://github.com/ObjectiveTruth/objectivetruth.github.io/blob/master/rebuild_tags.sh

Be sure to have the accompanying 14 line vim script in the /non_website_resources/ directory

AND

Make the /_layouts/tagpage.html shown in Christian's answer above but rename it to /_layouts/tag_pages.html

File structure should be like this:

.jekyll_website_root
├── _posts
├── _layout
│   ├── tag_pages.html
├── rebuild_tags.sh

Run from the root directory ./rebuild_tags.sh

If you get permission denied error be sure to run chmod 777 rebuild_tags.sh


If you look at scripts comments its fairly simple:

  • Uses sed to find all the tags in every .md file in _post directory

  • Uses sed to massage the data to proper format

  • Takes all the unique tags and makes a directory and a index.html for each

This way, if you have any new tags, just run the script to rebuild the pages before pushing to github

A nice simple non-plugin way to do tags


EDIT

Removed dependency on other files. Just need the one script!

ObjectiveTruth
  • 878
  • 10
  • 17
0

I do these with CSS. First lists an element and use the tag name as its id.

<span id="{{ site.posts | map: 'tags' | uniq | join: '"></span><span id="' }}"></span>

And then lists all the post and use its tags as a value for the "tags" custom attribute.

{% for post in site.posts %}
    <article class="post" tags="{% for tag in post.tags %}{{tag}}{% if forloop.last == false %}{{" "}}{% endif %}{% endfor %}">
        <h3><a href="{{post.url}}">{{post.title}}</a></h3>
    </article>
{% endfor %}

And then in CSS, hide all the posts by default, and only show posts with tags matches the url id/ hash

.post {
    display: none;
}
{% for tag in site.tags %}#{{tag[0]}}:target ~ [tags~={{tag[0]}}]{% if forloop.last == false %}, {% endif %}{% endfor %} {
    display: block;
}
/*
The compiled version will look like this
#tagname:target ~ [tags~="tagname"], #tagname2:target ~ [tags~="tagname2"] {
   display: block;
}
*/

I made an article about this here.

0

I've written a pre-push hook to detect missing tag pages. It will offer to create the missing tag pages with a bash script, gentag. The hook will also warn if pushing a draft.

For simplicity, it assumes tags are entered as a yaml array in front matter which are collected with yq.

There's also a pre-receive hook to deploy the site on a personal webserver.

Cole Tierney
  • 9,571
  • 1
  • 27
  • 35