Creating Custom Tags in Jekyll, with an actual example



Read the article on is a “service for concise, consistent, and legible badges in SVG and raster format, which can easily be included in GitHub readmes or any other web page.”

Their service takes query parameters to their API endpoint to generate a badge / shield. For example:


My GitHub

I was planning to use this in this website, when I realized a problem; it is hard to maintain. While it may be rare to change the content of those shields, it would be nice to have a maintainable way to put a shield on my blog.

Since I use Jekyll, I can define a custom ‘Liquid tag.’ Just like we can embed stuff from compatible services with liquid tags over at, e.g., {% github repo %} and {% embed website %}, we can make a custom tag, say, {% shields_io payload %}, to display a shield.

How does Liquid tag on Jekyll work?

The Liquid tag on Jekyll takes the following format:

{% tag_name [tag_argument] %}

It has a tag name, and one optional argument.

See the problem here? There is at most one value accepted as an argument. takes way more than that, and putting query parameter here does not solve anything.

There is still hope, though; let’s take a look at the code we’ll be writing.

module Jekyll
  class CustomTag < Liquid::Tag
    def initialize(tag_name, arg, parse_context)
      # @type [String]
      @arg = arg
    def render(_context)
      "The argument is #{@arg}" # return the render result here

The arg argument is the argument passed from the tag. It’s a string. So we can do something with the input.
And looking back at the original problem, we are trying to pass a set of key-value pairs to an API endpoint.

So, I decided to pass a JSON payload here; it can be prettified for our purposes, and Ruby supports JSON deserialization out of the box.

How to turn it into the URL

Since Ruby can turn the input JSON into a hash, we can iterate on this hash and construct the query parameter.

So the idea is to deserialize the JSON and store it in a variable:

    def initialize(tag_name, input, parse_context)
      # @type [Hash]
      @config = JSON.load(input.strip)

And construct the query parameter. I also decided to include href and alt for other purposes into the JSON payload, but they are not relevant for the request. So I extract their values and remove it from the input hash before turning the rest into the query parameter.

def render(_context)
  href = @config[:href]
  alt = @config[:alt]
  shield_tag = <<HTML
  <img src="{hash_to_query}"
  if alt != nil
    shield_tag += " alt=\"#{alt}\" />"
    shield_tag += " />"
  if href != nil
<a href="#{href}">

def hash_to_query { |k, v|
  }.join '&'


Here is my creation - copy the last two files and run a local server!