Tags — Taxonomy, Tag Pages, and Cross-Linking Content
Complete guide to the Dax Pro tag system: how tags work, why they must be lowercase, how tag pages are generated automatically, how to configure the /tags/ index, and how to use tags for SEO and content discovery.
What Tags Are
Tags are keywords attached to content. A blog post about static site generators might have tags dotnet, static, performance. A service page about migration might have tags migration, wordpress, dotnet. A post about Decap CMS might have tags decapcms, headless, git.
In Dax Pro, tags do three things:
1. Describe content — tags express what a piece of content is about, beyond its category
2. Create tag pages — every tag gets its own page at /tags/{tag}/ listing all content with that tag
3. Cross-link content — a reader interested in dotnet can click the tag and see every post and service about .NET
Tags are the backbone of content discovery. Unlike categories (which are cosmetic — a single label per post), tags are cross-cutting and bidirectional. One post can have many tags; one tag can apply to many posts.
This chapter covers:
1. How tags work in Dax Pro — the mechanics
2. The lowercase rule — why tags must be lowercase
3. Adding tags to posts and services — the syntax
4. The /tags/ index page — the taxonomy directory
5. The /tags/{tag}/ pages — individual tag pages
6. Configuring the tag controller — content/tags.md
7. Tags and SEO — how tags help search engines
8. Common mistakes — the patterns that break tags
How Tags Work in Dax Pro
The DAX engine builds the site in two phases. In the first phase, it reads all content files and collects metadata. In the second phase, it renders pages.
During the first phase, DAX reads the tags: field from every Markdown file in content/. It builds an internal map:
TagsMap = {
"dotnet": [post1.md, post2.md, service1.md, service3.md],
"static": [post1.md, post3.md],
"performance": [post1.md, post5.md, service4.md],
...
}
Each tag maps to a list of files that use it. This map is used in the second phase to generate tag pages.
During the second phase, DAX generates:
- One page per tag at
/tags/{tag}/, listing all content with that tag - One index page at
/tags/, listing all tags
The URL of a tag page is derived from the tag name. The tag dotnet becomes /tags/dotnet/. The tag performance becomes /tags/performance/. The tag name is lowercased and slugified — spaces become hyphens, non-alphanumeric characters are removed.
The display text of a tag is the tag name as written. If the tag is dotnet, it displays as dotnet. If it is performance, it displays as performance.
There is no separate "tag display name" field. What you write in the tags: field is what appears on the tag page and in the tag chip. This is why lowercase matters — it keeps the URL and the display consistent.
The Lowercase Rule
Tags must be lowercase. This is not a style preference — it is a technical requirement.
Here is what happens if you use uppercase:
tags: ["DotNet", "Razor"]
DAX generates the tag page at /tags/dotnet/ (lowercased) but the tags: field in your post contains DotNet and Razor. The tag chips on the post page link to /tags/DotNet/ and /tags/Razor/. Those URLs do not exist — the pages are at /tags/dotnet/ and /tags/razor/.
Result: every tag link on the post 404s. The tag page exists but is not reachable from the post.
The fix is always lowercase:
tags: ["dotnet", "razor"]
Now the tag chips link to /tags/dotnet/ and /tags/razor/, which are the URLs DAX generated. The tag pages work.
Why does DAX lowercase the URL but not the tags field? Because the URL is generated by the Slug() function, which lowercases everything. The tags: field is preserved as written, because DAX does not know whether the author intended DotNet or dotnet. This gives the author control over display, but requires discipline to keep display and URL consistent.
The easiest discipline: always write tags in lowercase. Then display and URL are always identical. No mismatch.
Adding Tags to Posts
In a blog post's frontmatter:
---
layout: "post.dax"
title: "Why Modern Enterprises Are Abandoning WordPress for .NET SSG"
description: "..."
date: 2026-03-28
tags: ["dotnet", "static", "wordpress", "performance", "migration"]
Syntax: a YAML list of strings.
["dotnet", "static"]tags:
- dotnet
- static
Both produce the same result. The inline array is more compact; the block list is easier to read when there are many tags.
Guidelines:
wordpress (broad), migration (specific), dotnet (technology), and static (architectural concept).dotnet tag, use it again for another post. Consistency builds the tag page.Adding Tags to Services
In a service page's frontmatter:
---
layout: "service.dax"
title: ".NET Static Site Generator Architecture"
description: "..."
index: "01. COMPILATION"
date: 2026-03-28
tags: ["dotnet", "razor", "blazor", "staticsitegenerator", "enterprise"]
Same syntax, same guidelines. Tags on services work exactly like tags on posts.
Services and posts share the same tag namespace. If a post and a service both have the dotnet tag, both appear on /tags/dotnet/. This is intentional — it means a reader interested in .NET sees both your blog articles and your service offerings.
In the default Dax Pro configuration, services have more technical tags (razor, blazor, azure, cicd) and posts have more marketing tags (enterprise, performance, migration). The overlap is intentional — it lets readers discover both types of content through shared tags.
The /tags/ Index Page
The /tags/ page is the taxonomy directory. It lists every tag used on the site, with the number of items that use each tag.
URL: /tags/
Generated from: content/tags.md (controller file)
Layout: tags.dax
Data available to the layout:
tags — list of all tag names (strings)all_tags — list of objects with name, slug, count, urlThe default tags.dax uses the tags list:
{% for tag in tags %}
<a href="/tags/{{ tag }}/">#{{ tag }}</a>
{% endfor %}
Each tag is a clickable chip linking to /tags/{tag}/.
The content/tags.md file:
---
layout: "tags.dax"
title: "Architecture & Subject Index"
breadcrumb: "Taxonomy Directory"
description: "Browse specialized technical subjects across the Dax Pro ecosystem."
No collection: field. Tags are not a collection in the traditional sense — they are generated from the tags: fields of all content. The content/tags.md file tells DAX to generate the /tags/ page, and its frontmatter provides the title and description.
To change the /tags/ page title and description, edit content/tags.md:
---
layout: "tags.dax"
title: "All Topics"
breadcrumb: "Topics"
description: "Browse all topics covered on this site."
Rebuild. The page updates.
To customize the visual appearance, edit templates/layouts/tags.dax. This is a developer task — see the Developer Guide.
The /tags/{tag}/ Pages
Every tag gets its own page. The page lists all content with that tag, paginated if necessary.
URL: /tags/{tag}/, where {tag} is the lowercased slugified tag name
Examples:
dotnet → /tags/dotnet/performance → /tags/performance/decapcms → /tags/decapcms/cicd → /tags/cicd/Layout: tag.dax
Data available to the layout:
tag — the current tag name (string)posts — list of all content with this tag (posts AND services, both included)title — auto-generated as "Tag: {tag}"description — auto-generated as "Posts tagged {tag}"The posts list includes both blog posts and services. A tag page for dotnet shows all posts AND all services with the dotnet tag. This is intentional — the tag page is a cross-section of content, not a blog archive.
Each item in posts is a page object with the same fields as any other page: title, description, url, image, date, category, author, etc.
The default tag.dax renders each item as a blog-style card:
{% for post in posts %}
<article class="dax-blog-card">
<img src="{{ post.image }}" alt="{{ post.title }}">
<h3><a href="{{ post.url }}">{{ post.title }}</a></h3>
<p>{{ post.description }}</p>
<a href="{{ post.url }}">Read Article →</a>
</article>
{% endfor %}
To change the appearance of tag pages, edit templates/layouts/tag.dax and templates/partials/tag/header.dax.
Configuring the Tag Controller
content/tags.md is the controller for /tags/. Its frontmatter provides the page metadata.
The frontmatter supports these fields:
| Field | Purpose |
|---|---|
layout |
Always tags.dax. Do not change. |
title |
Page title and H1 |
breadcrumb |
Short label for breadcrumb |
description |
Meta description |
No collection: field. Tags are generated automatically from tags: fields in content.
No pagination: field. The /tags/ index shows all tags on one page. There is no pagination for the tag index. If you have hundreds of tags, the page becomes long — but for most sites with 10–50 tags, one page is fine.
No date: field. The controller has no date. Ordering is alphabetical by tag name.
The body of content/tags.md is not rendered. Like all controllers, the body is ignored. All rendering comes from the layout.
Tags and SEO
Tags serve two SEO purposes:
1. Internal cross-linking
Every tag page links to every piece of content with that tag. This creates a dense internal link graph that search engines crawl. A post about wordpress links to /tags/wordpress/, which links to every other post about WordPress. The search engine follows these links and indexes all related content.
Why this matters: internal links distribute page authority. A new post linked from an established tag page inherits some of that tag page's authority. Over time, well-connected tag pages become hubs that pass authority to all their member posts.
2. Long-tail keyword capture
Tag pages are indexable. A tag page for decapcms is a page titled "Tag: decapcms" that lists all content about Decap CMS. Search engines index this page for the query "decapcms." If a reader searches for that term, the tag page may appear in results, exposing all your Decap CMS content.
This is why lowercase matters again: a tag page for DotNet would be indexed for "DotNet," not "dotnet." Search engines may still match, but exact-match keywords rank higher. Lowercase tags align with how people actually search.
3. Avoiding tag spam
Do not create a tag for every post. If every post has its own unique tag, every tag page lists exactly one post. This creates dozens of near-empty pages that search engines may treat as thin content.
Guidelines for SEO-safe tagging:
dotnet is better than dotnet-9.0-rc1. Broad tags accumulate content; narrow tags fragment.The rule of thumb: every tag should have at least three items on its tag page. If a tag has one item, either add more content with that tag or remove the tag.
Common Tag Mistakes
Mistake 1: Uppercase tags.
tags: ["DotNet", "Razor"] # wrong
tags: ["dotnet", "razor"] # correct
Uppercase tags produce 404 tag links. Always lowercase.
Mistake 2: Spaces in tags.
tags: ["content migration"] # wrong
tags: ["content-migration"] # correct
Spaces break URL slugs. Use hyphens.
Mistake 3: Tags with special characters.
tags: ["c++", "c#", ".net"] # problematic
tags: ["cpp", "csharp", "dotnet"] # correct
+, #, . are stripped by the slug function, which means c++ and c produce the same tag page. Use plain alphanumeric tags with hyphens.
Mistake 4: Too many tags per post.
tags: ["dotnet", "static", "performance", "migration", "wordpress", "drupal", "sitecore", "csharp", "razor", "blazor"] # too many
tags: ["dotnet", "migration", "static"] # good
Ten tags dilute the taxonomy. Two to six is the sweet spot.
Mistake 5: Unique tags.
# In post 1
tags: ["dotnet-9-preview"]
In post 2
tags: ["dotnet-9-ga"]
In post 3
tags: ["dotnet-9-lts"]
Three tags, each with one post. Three near-empty tag pages. Instead, use dotnet for all three — one strong tag page with three posts.
Mistake 6: Tags that duplicate category.
category: "Decap CMS"
tags: ["decapcms"]
The category already says Decap CMS. The tag adds nothing. Use tags for cross-cutting concepts, not redundant labels.
Mistake 7: Forgetting to rebuild after adding a tag.
Adding a newtag to a post creates a tag page at /tags/newtag/ — but only after a rebuild. If you add the tag and refresh the browser without building, the tag page 404s.
Mistake 8: Editing the tag controller for content changes.
If you want to add a new tag, edit the post's frontmatter. If you want to change a tag's display name, you cannot — the display is the tag name itself. If you want to change the tag page title format, that is a template change (tag.dax).
Mistake 9: Deleting a tag from a post but not from other posts.
If you remove dotnet from one post but keep it on ten others, the tag page for dotnet still exists (with ten posts). If you remove dotnet from the last post, the tag page disappears. This is expected. There is no "empty tag page" — the tag page exists only when at least one item uses the tag.
Mistake 10: Expecting tag pages to be editable via Decap CMS.
The tag controller (content/tags.md) is not in Decap CMS. Tags are derived automatically from content frontmatter. To add or remove a tag, edit the post or service. The /tags/ page updates on the next build.
Tag Workflow
A typical tag workflow for a content site:
1. Establish a base taxonomy before writing content.
Before writing your first post, decide on 10–20 tags that cover your content domain. For a .NET static site theme:
dotnet, csharp, static, performance, migration, wordpress,
drupal, sitecore, decapcms, headless, git, azure, cicd,
accessibility, wcag, seo, enterprise, theming, brand
These tags cover most of what you will write about. They are broad enough to accumulate content; specific enough to be useful.
2. Assign two to five tags per post from the base taxonomy.
A post about migrating from WordPress to Dax Pro:
tags: ["dotnet", "migration", "wordpress"]
A post about performance testing:
tags: ["dotnet", "static", "performance"]
3. Resist the temptation to add new tags for each post.
If you find yourself wanting to add a new tag for every post, the base taxonomy is not right. Revisit it. Add new tags only when a concept recurs across multiple posts.
4. Review your tag list quarterly.
Open content/tags.md or /tags/ on the live site. Look for tags with one item. Either add more content with that tag or remove it. Keep the taxonomy clean.
5. Never delete a tag from all its content at once.
If you decide to retire a tag, remove it from content one post at a time. As the last post loses the tag, the tag page disappears. This is a clean migration. If you delete all posts with a tag simultaneously, the tag page vanishes abruptly, which may break external links.
Verifying Tags Work
After adding tags to a post or service:
1. Rebuild — dotnet run -- build
2. Open the post — navigate to the post's URL, scroll to the bottom, confirm the tag chips appear
3. Click a tag chip — navigate to the tag page, confirm it loads
4. Check the tag page — the post you just edited appears in the list
5. Check /tags/ — the tag appears in the tag directory, with the correct count
6. Check the URL — lowercase, hyphenated, matches the tag name
If a tag chip 404s:
If the tag page loads but is empty:
If /tags/ does not show a tag:
What You Have Now
After this chapter, you can:
/tags/ index page/tags/{tag}/ pagescontent/tags.md)Tags are a small feature with an outsized impact on SEO and content discovery. Used well, they connect everything you write. Used carelessly, they create dozens of near-empty pages that dilute your site.
The next chapter is Images & Media — how to add images to your content, the formats that work best, and how to avoid the most common image-related mistakes.
→ Continue to Images & Media