Basker Docs

Tag template

Reference for the tag object — free-form post labels

The tags template renders pages that display all posts matching one or more tags. Tag pages are served by a dedicated route rather than the standard template lookup.

Location

└── theme
    └── templates
        └── tags.liquid

URL Structure

Tags are scoped to a blog, so tag pages are accessible at:

  • /blogs/:blogSlug/tags/:tagged - Single tag
  • /blogs/:blogSlug/tags/:tag+tag2 - Multiple tags (AND logic)

For example:

  • /blogs/news/tags/featured - Posts tagged "featured"
  • /blogs/news/tags/news+featured - Posts tagged BOTH "news" AND "featured"

Each tag record exposes a ready-built url property in exactly this shape. Prefer linking with the url property rather than assembling the path by hand, so links stay correct if the URL structure ever changes.

Template Variables

The tags template receives these variables:

The tags Array

The tag slugs taken from the URL — an array of strings, one per tag being filtered:

PropertyTypeDescription
tagsarrayTag slugs being filtered (strings)

On the tag archive page the tags variable holds the raw slugs from the URL (so a single-tag page gives ["featured"]). This is different from a post's own tags, which are full objects — see the warning below.

The posts Array

An array of posts matching ALL specified tags:

PropertyTypeDescription
postsarrayPosts matching the tag filter

Each post in the array has access to all standard post properties.

A post's own tags and categories are arrays of objects, not plain strings. Each entry has a title and a slug — loop over them and read tag.title and tag.slug. You do not need handleize; use tag.slug directly when building links.

Basic Template Example

{% layout 'layouts/default.liquid' %}

{% capture content_for_layout %}
<div class="tags-page">
  <header class="tags-header">
    <h1>
      Posts tagged:
      {% for tag in tags %}
        <span class="tag-badge">{{ tag }}</span>
        {% unless forloop.last %} + {% endunless %}
      {% endfor %}
    </h1>
    <p class="post-count">{{ posts.size }} {% if posts.size == 1 %}post{% else %}posts{% endif %}</p>
  </header>

  <div class="posts-list">
    {% if posts.size > 0 %}
      {% for post in posts %}
        <article class="post-card">
          {% if post.image %}
            <img
              src="{{ post.image | image_url: width: 400, height: 250 }}"
              alt="{{ post.title }}"
              loading="lazy"
            >
          {% endif %}

          <div class="post-card__content">
            <h2>
              <a href="/posts/{{ post.slug }}">{{ post.title }}</a>
            </h2>

            {% if post.publishDate %}
              <time datetime="{{ post.publishDate }}">
                {{ post.publishDate | date: '%B %d, %Y' }}
              </time>
            {% endif %}

            {% if post.description %}
              <p>{{ post.description | truncate: 150 }}</p>
            {% endif %}
          </div>
        </article>
      {% endfor %}
    {% else %}
      <p class="no-posts">No posts found with these tags.</p>
    {% endif %}
  </div>
</div>
{% endcapture %}

Working with Tags

Display Current Tags

<div class="active-tags">
  <span>Filtering by:</span>
  {% for tag in tags %}
    <span class="tag tag--active">{{ tag }}</span>
  {% endfor %}
</div>

Tag Pills with Remove Option

The page-level tags are slugs, so you can rebuild the +-joined path directly. Take the blog slug from any post so the links stay scoped to the current blog:

{% assign blog_slug = posts.first.blog.slug %}
<div class="tag-filters">
  {% for tag in tags %}
    {% assign other_tags = '' %}
    {% for t in tags %}
      {% if t != tag %}
        {% if other_tags != '' %}
          {% assign other_tags = other_tags | append: '+' %}
        {% endif %}
        {% assign other_tags = other_tags | append: t %}
      {% endif %}
    {% endfor %}

    <span class="tag-pill">
      {{ tag }}
      {% if other_tags != '' %}
        <a href="/blogs/{{ blog_slug }}/tags/{{ other_tags }}" class="remove-tag" aria-label="Remove {{ tag }} filter">×</a>
      {% else %}
        <a href="/blogs/{{ blog_slug }}" class="remove-tag" aria-label="Remove {{ tag }} filter">×</a>
      {% endif %}
    </span>
  {% endfor %}

  {% if tags.size > 1 %}
    <a href="/blogs/{{ blog_slug }}" class="clear-all">Clear all</a>
  {% endif %}
</div>

Show All Tags on Each Post

A post's tags are objects with title and slug. Build the archive URL from the post's blog slug and the tag slug, and compare tag.slug against the page-level tags slugs to highlight the active ones:

{% for post in posts %}
  <article class="post-card">
    <h3><a href="/posts/{{ post.slug }}">{{ post.title }}</a></h3>

    {% if post.tags.size > 0 %}
      <div class="post-tags">
        {% for tag in post.tags %}
          <a href="/blogs/{{ post.blog.slug }}/tags/{{ tag.slug }}"
             class="tag {% if tags contains tag.slug %}tag--active{% endif %}">
            {{ tag.title }}
          </a>
        {% endfor %}
      </div>
    {% endif %}
  </article>
{% endfor %}

Complete Template Example

{% layout 'layouts/default.liquid' %}

{% capture content_for_layout %}
{% assign blog_slug = posts.first.blog.slug %}
<div class="tags-page">
  {# Breadcrumb navigation #}
  <nav class="breadcrumb" aria-label="Breadcrumb">
    <ol>
      <li><a href="/">Home</a></li>
      <li><a href="/blog">Blog</a></li>
      <li aria-current="page">
        Tagged:
        {% for tag in tags %}
          {{ tag }}{% unless forloop.last %}, {% endunless %}
        {% endfor %}
      </li>
    </ol>
  </nav>

  {# Page header #}
  <header class="tags-header">
    <h1>
      {% if tags.size == 1 %}
        Posts tagged "{{ tags.first }}"
      {% else %}
        Posts tagged:
      {% endif %}
    </h1>

    {# Active tag filters #}
    {% if tags.size > 1 %}
      <div class="active-filters">
        {% for tag in tags %}
          {% comment %} Build URL without this tag {% endcomment %}
          {% assign remaining_tags = '' %}
          {% for t in tags %}
            {% if t != tag %}
              {% if remaining_tags != '' %}
                {% assign remaining_tags = remaining_tags | append: '+' %}
              {% endif %}
              {% assign remaining_tags = remaining_tags | append: t %}
            {% endif %}
          {% endfor %}

          <span class="filter-tag">
            {{ tag }}
            {% if remaining_tags != '' %}
              <a href="/blogs/{{ blog_slug }}/tags/{{ remaining_tags }}" class="remove" title="Remove this filter">×</a>
            {% else %}
              <a href="/blogs/{{ blog_slug }}" class="remove" title="Remove this filter">×</a>
            {% endif %}
          </span>
        {% endfor %}

        <a href="/blogs/{{ blog_slug }}" class="clear-filters">Clear all filters</a>
      </div>
    {% endif %}

    <p class="results-count">
      {{ posts.size }} {% if posts.size == 1 %}result{% else %}results{% endif %}
    </p>
  </header>

  {# Posts listing #}
  {% if posts.size > 0 %}
    <section class="tags-posts">
      <div class="post-grid">
        {% for post in posts %}
          <article class="post-card">
            <a href="/posts/{{ post.slug }}" class="post-card__link">
              {% if post.image %}
                <img
                  src="{{ post.image | image_url: width: 400, height: 250 }}"
                  alt="{{ post.title }}"
                  class="post-card__image"
                  loading="lazy"
                >
              {% endif %}

              <div class="post-card__content">
                {% if post.categories.size > 0 %}
                  <span class="post-card__category">{{ post.categories.first.title }}</span>
                {% endif %}

                <h2 class="post-card__title">{{ post.title }}</h2>

                <div class="post-card__meta">
                  {% if post.publishDate %}
                    <time datetime="{{ post.publishDate }}">
                      {{ post.publishDate | date: '%b %d, %Y' }}
                    </time>
                  {% endif %}

                  {% if post.authors.size > 0 %}
                    <span class="author">by {{ post.authors.first.name }}</span>
                  {% endif %}
                </div>

                {% if post.description %}
                  <p class="post-card__excerpt">
                    {{ post.description | truncate: 120 }}
                  </p>
                {% endif %}

                {# Show post tags, highlighting active ones #}
                {% if post.tags.size > 0 %}
                  <div class="post-card__tags">
                    {% for tag in post.tags %}
                      <span class="mini-tag {% if tags contains tag.slug %}mini-tag--active{% endif %}">
                        {{ tag.title }}
                      </span>
                    {% endfor %}
                  </div>
                {% endif %}
              </div>
            </a>
          </article>
        {% endfor %}
      </div>
    </section>

  {% else %}
    <div class="tags-empty">
      <h2>No Posts Found</h2>
      <p>
        No posts match
        {% if tags.size == 1 %}
          the tag "{{ tags.first }}".
        {% else %}
          all of these tags: {{ tags | join: ', ' }}.
        {% endif %}
      </p>
      <p>Try removing some filters or browse all posts.</p>
      <a href="/blog" class="btn btn--secondary">Browse All Posts</a>
    </div>
  {% endif %}

  {# Navigation #}
  <nav class="tags-nav">
    <a href="/blog" class="back-link">
      &larr; Back to Blog
    </a>
  </nav>
</div>
{% endcapture %}

{% schema %}
{
  "name": "tags",
  "settings": [
    {
      "type": "select",
      "name": "layout",
      "label": "Posts Layout",
      "options": [
        { "value": "grid", "label": "Grid" },
        { "value": "list", "label": "List" }
      ],
      "defaultValue": "grid"
    },
    {
      "type": "checkbox",
      "name": "show_post_tags",
      "label": "Show Tags on Each Post",
      "defaultValue": true
    }
  ]
}
{% endschema %}

Linking to Tags

From Post Templates

A post's tags are objects with title and slug. Build the archive URL from the post's blog slug and the tag slug — the same shape core generates:

{# In post.liquid template #}
{% if post.tags.size > 0 %}
  <div class="post-tags">
    <span class="tags-label">Tags:</span>
    {% for tag in post.tags %}
      <a href="/blogs/{{ post.blog.slug }}/tags/{{ tag.slug }}" class="tag">{{ tag.title }}</a>
    {% endfor %}
  </div>
{% endif %}

Combining Tags

Join multiple tag slugs with + for an AND filter. Tag pages are blog-scoped, so include the blog slug:

{# Link to posts with multiple tags #}
<a href="/blogs/news/tags/featured+world-premiere">Featured premieres</a>
<a href="/blogs/news/tags/interview+behind-the-scenes">Behind-the-scenes interviews</a>

Tag Cloud Component

When you have the full tag records (for example via the blog's tags list), each one carries a ready-built url, so you can link to it directly:

{# Display all available tags #}
{% if blog.tags.size > 0 %}
  <aside class="tag-cloud">
    <h3>Browse by Tag</h3>
    <div class="tags">
      {% for tag in blog.tags %}
        <a href="{{ tag.url }}" class="tag">
          {{ tag.title }}
        </a>
      {% endfor %}
    </div>
  </aside>
{% endif %}

Combining Tags vs Categories

FeatureTagsCategories
URL/blogs/:blogSlug/tags/:tagged/blogs/:blogSlug/categories/:slug
Multiple filterYes (+ syntax)No
HierarchyFlatFlat
User-definedYesAdmin-defined
Best forFlexible labelingPrimary classification

Alternate Tags Templates

Create variations for different tag presentations:

templates/
├── tags.liquid           # Default tags template
├── tags.minimal.liquid   # Simple list view
└── tags.magazine.liquid  # Magazine-style layout

On this page