A tag cloud lets readers browse posts by topic, with more frequently used tags shown a little larger. Jekyll exposes post tags through Liquid, so you can render a cloud with a template and CSS instead of installing a cloud plugin. The template creates links, not tag archive pages: make sure your site already generates a page for every tag.
Learn more about Jekyll, or, if you are moving a blog, see our guide to Jekyll and GitHub Pages. You can also browse the site's Jekyll posts.
Add tags to Jekyll posts
Jekyll reads tags from a post's YAML front matter. Use a list when you have multiple tags:
---
title: A Jekyll post
tags:
- jekyll
- liquid
---
Jekyll makes a site.tags collection available to templates. Each entry contains a tag name and the posts that use it, as described in the Jekyll variables reference. Keep tag spelling and capitalization consistent across posts so the cloud does not show separate entries for variants such as Jekyll and jekyll.
Render a tag cloud with Liquid
Add this snippet to a layout or include where you want the cloud to appear. This example assumes that tag archive pages use URLs like /tag/jekyll/:
{% assign tags = site.tags | sort %}
{% assign tag_count = tags | size %}
{% if tag_count > 0 %}
<nav aria-label="Posts by tag">
<ul class="tag-cloud">
{% for tag in tags %}
{% assign tag_name = tag.first %}
{% assign post_count = tag.last | size %}
{% assign size_level = post_count | at_most: 5 %}
{% capture tag_path %}/tag/{{ tag_name | slugify }}/{% endcapture %}
<li>
<a class="tag-cloud__link tag-cloud__link--{{ size_level }}"
href="{{ tag_path | relative_url | escape }}">
<span>{{ tag_name | escape }}</span>
<span class="tag-cloud__count">({{ post_count }})</span>
</a>
</li>
{% endfor %}
</ul>
</nav>
{% else %}
<p>No tags to browse yet.</p>
{% endif %}
sort orders the tag entries by name. tag.first is the name and tag.last is the list of matching posts, so size gives the count. The count is capped at five for the CSS class: every tag used by five or more posts gets the largest size, rather than growing without a limit. These are standard Liquid operations, and Jekyll documents its relative_url and slugify filters.
The archive path is site-specific. Change /tag/ to match your permalink structure, and use the same slug-generation rule as your tag pages. relative_url adds the configured baseurl, which matters when a site is published under a project subpath. Escaping the generated href and visible tag name keeps the output safe when tag names contain special characters.
Style the cloud
Add these rules to your site's stylesheet and adjust the sizes to fit your theme. The five classes correspond to the capped post-count levels in the Liquid template.
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1rem;
list-style: none;
margin: 0;
padding: 0;
}
.tag-cloud__link {
display: inline-flex;
align-items: baseline;
gap: 0.3em;
}
.tag-cloud__link--1 { font-size: 0.9rem; }
.tag-cloud__link--2 { font-size: 0.95rem; }
.tag-cloud__link--3 { font-size: 1rem; }
.tag-cloud__link--4 { font-size: 1.1rem; }
.tag-cloud__link--5 { font-size: 1.2rem; }
.tag-cloud__count {
font-size: 0.8em;
}
.tag-cloud__link:focus-visible {
outline: 2px solid currentColor;
outline-offset: 0.2em;
}
The visible post count means size is not the only signal of tag frequency. The navigation landmark and list give screen-reader users a clear group of links, while the focus outline makes keyboard focus easier to see. Keep the size differences modest so every tag remains readable.
Use a plain tag list instead
A cloud is optional. If you have many tags, or want every item to have equal visual weight, use the same Jekyll data in a simple list:
{% assign tags = site.tags | sort %}
<ul>
{% for tag in tags %}
{% assign tag_name = tag.first %}
{% capture tag_path %}/tag/{{ tag_name | slugify }}/{% endcapture %}
<li>
<a href="{{ tag_path | relative_url | escape }}">{{ tag_name | escape }}</a>
({{ tag.last | size }})
</li>
{% endfor %}
</ul>
This keeps tag navigation scannable without using font size to encode popularity.
Check tag archive pages and GitHub Pages support
Jekyll exposes tags to Liquid, but it does not automatically create a separate archive page for each tag. The links above only work if your site generates matching pages. If you generate those pages yourself, use the same slugify behavior and URL prefix as the cloud.
The cloud itself needs no custom plugin. GitHub Pages supports Jekyll and a set of approved plugins, but it cannot build a site with an unsupported plugin. Check GitHub's current Jekyll and Pages guidance before depending on a generator plugin; otherwise, create the archive pages yourself or build the static site locally. See the GitHub Pages site for hosting information.
If a tag link returns a 404, compare its final path with the actual archive page, including capitalization, punctuation, and any baseurl. For general Liquid sorting behavior, see the Liquid sort filter documentation.