↓Skip to main content

Alphabetizing Jekyll Page Tags In Pure Liquid (Without Plugins)

·3 mins

When you host a Jekyll site on GitHub , it renders the site in safe-mode. This means you can’t use any plugins enhance Jekyll’s functionality and, more specifically, you can’t write a Liquid tag that alphabetizes the site’s tags. Unordered tags make a Jekyll tag list look sloppy, so if you care about the details, you likely want your tag list to be alphabetized.

It’s easy to alphabetize the tag list on a given page (just list the tags alphabetically), but getting your tags.html to list tags alphabetically is a bit more convoluted if you aren’t able to use plugins.

Waist-Deep in Liquid #

Liquid provides some pretty good documentation for figuring out what logical operators and functions are built into the templating language. Particularly useful are capture and assign, which each capture values as variables. These logical operators can contain additional logic like for-loops, splits, sorts and joins. Here’s the code I use to generate a sorted list of tags:

{% capture site_tags %}{% for tag in site.tags %}{{ tag | first }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
{% assign tag_words = site_tags | split:',' | sort %}

Line 1 above will get the tag name for every tag on the site and set them to the site_tags variable. Each tag object contains both the tag name and a list of the associated posts. This capture statement is all on one line so that the commas can reliably be used as delimiters. Line 2 creates the tag_words variable that is a sorted array of the tag names. If you want to see what these two statements would produce within Jekyll, you’ll find them printed in html comments on my tags page .

Building The HTML #

As you can see on tags.html , the tags are listed alphabetically with post counts and then each tag’s post is listed below. Here’s the Liquid code:

<div id="tags">
  <h1>Tags</h1>
  <ul class="tag-box inline">
  {% for item in (0..site.tags.size) %}{% unless forloop.last %}
    {% capture this_word %}{{ tag_words[item] | strip_newlines }}{% endcapture %}
    <li><a href="#{{ this_word | cgi_escape }}">{{ this_word }} <span>{{ site.tags[this_word].size }}</span></a></li>
  {% endunless %}{% endfor %}
  </ul>

  {% for item in (0..site.tags.size) %}{% unless forloop.last %}
    {% capture this_word %}{{ tag_words[item] | strip_newlines }}{% endcapture %}
  <h2 id="{{ this_word | cgi_escape }}">{{ this_word }}</h2>
  <ul class="posts">
    {% for post in site.tags[this_word] %}{% if post.title != null %}
    <li itemscope><span class="entry-date"><time datetime="{{ post.date | date_to_xmlschema }}" itemprop="datePublished">{{ post.date | date: "%B %d, %Y" }}</time></span> &raquo; <a href="{{ post.url }}">{{ post.title }}</a></li>
    {% endif %}{% endfor %}
  </ul>
  {% endunless %}{% endfor %}
</div>

On line 4 you’ll notice an unless forloop.last. This is because it’s a quick an easy way to keep the array index in bounds. Lines 6 and 14 show how you can use a tag’s name to pull the tag from site.tags. I checked for post.title != null because I don’t want to display pages without titles.

TL;DR #

Alphabetizing Jekyll site tags is trickier than it should be. If you plan to deploy the site via GitHub Pages, you can’t use plugins. The source code for my pure-Liquid, alphabetized tags is on GitHub and deployed here .


Comments (20)

Archived from Disqus — comments are closed. Want your comment removed? Open an issue.

  1. David Hutchison

    Thanks for this work, doing anything involving sorting in Liquid is so much more painful than it should be!

    I'm running a slightly modified version of this that makes the sorting of tags case insensitive. It is not very efficient but I don't think it can be improved on with the current filters available. Source is at https://github.com/dhutchis... .

    1. Rugal Bernstein

      very nice!! thanks for you improvement!

  2. Aurelio Jargas

    Thanks for the excellent post. I really enjoyed your all-in-one tag page.

    Testing it locally I've noticed that the "num_words" variable is not necessary, one can use directly the "site.tags.size" property. Or maybe I'm missing something?

    {% for item in (0..site.tags.size) %}

    1. LanyonM

      I've tested locally as well and you are correct - the num_words variable is not needed. Thanks for the improvement! I'll update the post in the coming days.

      1. LanyonM

        It took me longer to get to it than intended, but the post (and site) has been updated. Thanks again for the suggestion!

    2. Róbert Papp

      Improving on this, how about {% for tag in tag_words %}? Then tag=tag name, and site.tags[tag]=posts; it looks cleaner.

  3. Awesome tip, I have been looking for this for a while.

  4. acdcjunior

    Great tip, thanks for sharing!

  5. Bruno Pinheiro

    This is working for me:

    {% assign sorted_tags = (site.tags | sort:0) %}
    {% for tags in sorted_tags %}

    1. Michael Ransley

      This did the business for me, thank you for the post and your comment.

    2. Chuck Grimmett

      You rock, Bruno.

    3. Chetabahana

      It works. Thanks a lot. :)

  6. I can confirm that the syntax:
    {% assign sorted_tags = (site.tags | sort:0) %}
    {% for tag in sorted_tags %}
    <li>{{ tag | first }}</li>
    {% endfor %}
    works perfect.

    Thank you very much Michael!

  7. Róbert Papp

    That capture with | strip_newlines looks paranoid, there's no new line in the creation of tag_words, why do you need to trim it? Even if you do {% assign this_word = tag_words[item] | strip_newlines %} may be simpler.

  8. Oskar Klintrot

    Great solution, works like a charm!

  9. Business Science

    Worked perfectly! Thanks for this. :)

  10. Jingying Zhou

    thank you! but I ran into a problem that, when I have different collections (say, _posts and _poetry) only tags under _posts were counted

    1. Róbert Papp

      See https://jekyllrb.com/docs/v... "site.tags.XXX=The list of all Posts with tag XXX.". Notice it says "Posts" not "every document". site.tags is a map from tags to a list of posts.

      Check out http://stackoverflow.com/q/... and https://talk.jekyllrb.com/t... for more info on collection tags (a feature not directly supported by Jekyll)

      1. Jingying Zhou

        Thanks Róbert for the links and explanations. It seems that only posts would be counted from site.tags and collection tags should be treated in a different manner.

  11. Tomek Bielaszewski

    Thanks, that's what I was looking for