# Tags

> The other way of grouping things: a word on a post that a list can filter by. What a tag is, what it deliberately is not, and how to build a page for one.
>
> From the MightyBuilder documentation: https://docs.mightybuilder.ai/dynamic/tags

A page lives somewhere — in your nav, inside a section, at an address that says
where it sits. An article lives nowhere in particular: it arrives, and the only
thing that orders it is its date.

A tag is the other way of grouping things. It is a word you put on a post, and a
list can filter by it.

## What a tag is, and what it is not

A word. `recipes`, `news`, `case-study`.

There is no tag record anywhere in the product. A tag has no description, no
picture of its own, no label separate from the word, and no order you arrange
them in. There is no screen listing them and nothing to tidy up.

**The tags your site has are the tags on its posts.** Take the last one off the
last article and it has stopped existing. That is the whole design, and what it
buys is that there is nothing to keep in step.

## The shape

Lowercase letters, digits, and hyphens where you would put a space:
`case-study`, never `Case Study`. At most 20 tags on a post, at most 40
characters each.

The box slugifies as you type, so typing `Sourdough Tips` gives you
`sourdough-tips` before you have saved anything.

It matters because **a template matches a tag exactly**. A list filtered on
`Recipes` against an article tagged `recipes` is not an error and not a warning —
it is an empty list on a page that looks perfectly fine.

## Putting one on

Open any page, article, block or form, go to its **Settings** tab, and the Tags
box is in the right-hand column under Picture. Type and press Enter, or a comma.
Backspace on an empty box takes the last one back, and the × on a chip removes
it.

**It suggests the tags you already have.** Start typing and your site's own tags
appear under the box; ↑ and ↓ move through them and Enter or Tab takes one.

That is worth using rather than typing past. `recipe` on one article and
`recipes` on the next are two different tags, not one — and the page listing one
of them will be quietly half-empty, which is exactly the kind of wrong that
nobody notices.

## Blocks and forms have them too

Nothing on your site reads a block's tags or a form's. They are there for the
admin's own lists: once you have forty blocks, a tag is how you tell
"Header (old)" from "Header".

## Listing one tag

```liquid
{% assign recipes = collections.articles | where: "tags", "recipes" %}
{% for post in recipes %}
  <h3><a href="{{ post.url }}">{{ post.title }}</a></h3>
{% else %}
  <p>Nothing here yet.</p>
{% endfor %}
```

Pages work the same way — `site.pages | where: "tags", "guide"` — and the same
rule applies as everywhere else: build the list with `assign` first, then loop
it. See [Sorting and filtering](/dynamic/sorting-and-filtering).

## Showing a post's own tags

```liquid
{% if page.tags.size > 0 %}
  <ul class="tags">
    {% for tag in page.tags %}
      <li>{{ tag | replace: "-", " " | capitalize }}</li>
    {% endfor %}
  </ul>
{% endif %}
```

`{{ tag | replace: "-", " " | capitalize }}` is how you turn `slow-bread` into
`Slow bread` for a reader. There is no separate display name to fill in — the
word is the whole tag, and the display is the template's job.

Every tag also becomes a class on the page's `<body>`, as `mb-tag-recipes`, so
your site stylesheet can style everything tagged one way without any page
needing to know.

## A tag page is an ordinary page

Nothing generates `/blog/tag/recipes` for you. If you want a page for a tag, you
make one: a page called Recipes, with the loop above on it. It is in your nav,
in your sitemap, and it has its own title, description and whatever else you
want to say at the top of it — because it is a page like every other page.

That is a decision rather than something missing. A generated address is one the
product has to name, cache, invalidate and keep working when you rename the tag
it was built from — and what you would get for all of that is a page you cannot
write a word of.

## A list of every tag

`site.tags` is every tag on any page or article, sorted, with nothing repeated:

```liquid
{% for tag in site.tags %}
  <a href="/blog?tag={{ tag }}">{{ tag | replace: "-", " " | capitalize }}</a>
{% endfor %}
```

Where those links should point depends on what you built. If you made a page per
tag, link to those pages by their real addresses. If you would rather have one
index that narrows itself, `?tag=` on the end of its own address is the shape to
use — a tag is slug-shaped precisely so it can sit in a link with nothing
escaped — and a few lines of site JavaScript can hide the cards that do not
match.

## What goes wrong

**A tag nobody uses.** A list filtered on a tag that is on no page or article
publishes with a warning naming the tags you do have. It is a warning rather
than a refusal because a topic you have not written yet is a perfectly good
empty list — but it is also what a retag leaves behind, and that one is worth
reading.

**`where` with nothing to match.** `where` needs a field *and* a value.
Publishing refuses one with only a field and tells you so. See
[When something is wrong](/dynamic/when-something-is-wrong).

**A tag is not a field on the post.** `page.tags` is a list of words, so
`page.tags.size`, `page.tags.first` and `page.tags.last` work — anything else
off it, like `page.tags.name`, will not publish. To read the words themselves,
loop over the list or use `| join`.
