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.

  • Inline array: ["dotnet", "static"]
  • Block list:
  • 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:

  • Two to six tags per post. Fewer than two limits discovery. More than six dilutes the taxonomy.
  • Mix broad and specific. A post about migrating WordPress to Dax Pro might have wordpress (broad), migration (specific), dotnet (technology), and static (architectural concept).
  • Reuse existing tags. If you already have a dotnet tag, use it again for another post. Consistency builds the tag page.
  • Do not use tags as categories. Categories are for one-dimensional classification. Tags are for multi-dimensional discovery. A post can have one category and five tags.

  • 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, url
  • The 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:

  • Tag dotnet → /tags/dotnet/
  • Tag performance → /tags/performance/
  • Tag decapcms → /tags/decapcms/
  • Tag 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:

  • Reuse tags across multiple posts. A tag should apply to at least three posts to be useful.
  • Delete tags that only apply to one post. If a post's tag is unique, remove it. The post is discoverable through its category and title; a unique tag adds nothing.
  • Prefer broad tags over narrow ones. 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:

  • The tag is uppercase — lowercase it
  • The tag has spaces — replace with hyphens
  • The build has not run since adding the tag — rebuild
  • If the tag page loads but is empty:

  • The tag page is stale — rebuild
  • The tag was removed from all content — the tag page should not exist; check if there is an old build artifact
  • If /tags/ does not show a tag:

  • The tag is on content but has not been rebuilt — rebuild
  • The tag is malformed (special characters) — clean it up

  • What You Have Now

    After this chapter, you can:

  • ✅ Understand how tags work in Dax Pro
  • ✅ Add tags to posts and services
  • ✅ Use tags correctly (lowercase, no spaces, no special characters)
  • ✅ Understand the /tags/ index page
  • ✅ Understand the /tags/{tag}/ pages
  • ✅ Configure the tag controller (content/tags.md)
  • ✅ Use tags for SEO without creating thin pages
  • ✅ Follow a tag workflow that keeps the taxonomy clean
  • ✅ Verify tags work after adding content
  • ✅ Fix the ten most common tag mistakes
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