# Designing a Taxonomy That Scales

> Source: https://agilitycms.com/docs/developers/designing-a-taxonomy

A taxonomy is the set of labels you use to group and find content: categories, tags, topics, audiences, regions, product lines. Get it right and editors file content in seconds, your site can filter and build landing pages from it, and new channels can reuse it. Get it wrong and you end up with three spellings of every tag and filters that miss half the content.

In Agility you build a taxonomy from the same parts as the rest of your content model: Drop-down List fields, content lists, and Linked Content fields. This page shows how to choose between them and how to keep the result healthy as it grows.

## Choose the right tool for each dimension

Treat each way of grouping content (topic, audience, region, format) as its own **dimension**, and pick a tool per dimension.

| Tool | How it works | Who changes the terms | Best for |
| --- | --- | --- | --- |
| **Drop-down List field** | Fixed choices defined in the model; the API returns the selected choice's value | Developers or admins, by editing the model | Small, stable sets your code depends on: content type, layout variant, priority |
| **Linked Content to a terms list, single** (Dropdown List render type) | Each term is an item in a content list; the item stores one term's content ID | Editors, by adding items to the list | One category per item |
| **Linked Content to a terms list, many** (Search List Box render type) | As above, storing a comma-separated list of term IDs | Editors | Tags and topics, several per item |
| **Structure** (separate lists, sitemaps) | Where the content lives is the classification | Developers or admins | A dimension where every item belongs to exactly one value, such as a destination site |

Rules of thumb:

- **Use a Drop-down List** when adding a term should involve a developer anyway, because the code has to handle it. The stored values are stable strings your code can switch on, and editors see the labels.
- **Use a terms list** when editors should be able to add terms without a code change, when terms need their own fields (a description, a slug, an image), or when a term has its own page.
- **Use structure** when an item can only ever have one value. A selector that always holds the same answer is one more thing to get wrong. See [Publishing to Multiple Destinations](/docs/overview/publishing-to-multiple-destinations) for the trade-offs when the dimension is "which channel".

## Model a terms list

The basic setup is described in [Tagging Content in Agility](/docs/developers/tagging-content-in-agility): a content model for the term, a content list built from it, and a Linked Content field on the content you want to classify. For a taxonomy that will grow, give the term model a few more fields than a title:

| Field | Type | Why |
| --- | --- | --- |
| `Title` | Text | The label editors and visitors see |
| `Slug` | Text | A stable, URL-safe key for term pages and filters in URLs |
| `Description` | Multi-Line Text | What belongs under this term, so editors file content consistently. It also helps search engines and AI agents |
| `Active` | True/False (optional) | Retire a term without deleting it |

Then add the Linked Content field to each model that uses the taxonomy:

- **Link to:** Content
- **Link Type:** Specific Item(s) from a List
- **Render UI:** Search List Box for tags (Dropdown List for a single category)
- **Content Reference:** your terms list
- **Save Value to Field / Save Text to Field:** new hidden fields, for example `Tags_ValueField` and `Tags_TextField`

Use one terms list per dimension (`Topics`, `Audiences`, `Regions`) rather than one big `Tags` list with prefixes like `audience-` and `region-`. Separate lists keep each picker short and let you give each dimension its own fields and rules.

### Hierarchy

Most taxonomies only need one level. If you need two (for example *Category > Subcategory*), model each level as its own list, and give the lower level a Dropdown List link to its parent. Content then links to the most specific level, and your front end walks up to the parent. Avoid deeper trees unless you have a real navigation need: every extra level is more for editors to maintain and more for code to resolve.

## Querying by taxonomy

The companion value field is the cheapest way to filter, because it stores plain content IDs and needs no expansion.

For a single category:

```text
fields.category_ValueField[eq]"110"
```

For tags (a comma-separated list of IDs), use `contains`:

```text
fields.tags_ValueField[contains]"32"
```

Test `contains` against your own data before you build on it, so you know exactly which items match. Operator details are in [GraphQL & Rest API Filtering](/docs/developers/graphql-operators).

Other things to know:

- **Render names from the linked items, not from the text field.** The text companion field is written when an item is saved. Read the term's title from the linked item (or fetch the terms list once and look terms up by ID), so a renamed term shows its new name everywhere.
- **Fetch terms lists in full.** Pass `take` on every list request: the Content Fetch API returns 10 items by default and GraphQL lists return 50. The maximum per request is 250.
- **Term IDs work across locales.** When you copy a term into another locale, the copy keeps the same `contentID`, so the IDs stored on your content still match. Translate the term's title per locale, and consider marking the `Slug` field **Constant across all languages** if your URLs use the same slug in every locale. See [Choosing a Localization Strategy](/docs/developers/choosing-a-localization-strategy).

## Governance: keeping it clean

Taxonomies decay through small, reasonable-looking additions. Decide these before launch:

1. **One owner per dimension.** Someone decides whether a new term is needed, merges duplicates and retires unused terms.
2. **Naming rules.** Singular or plural, capitalization, no abbreviations. Write them in the term model's field descriptions so editors see them.
3. **A definition for every term.** If two terms can't be told apart by their descriptions, merge them.
4. **A limit per item.** For example "at most five tags". Make it an editorial rule and check it in review.
5. **Retire before you delete.** Mark a term inactive first. Before deleting a term, filter on the value field to find items that use it, and re-tag them. Stored IDs aren't removed from other items for you.
6. **A review rhythm.** Once a quarter, list terms with few or no items and decide whether to merge or retire them.

## Common mistakes

- **Tags typed as free text.** Every variation becomes a new tag. Link to a terms list instead.
- **One `Tags` list doing four jobs.** Split it by dimension.
- **A Checkbox List on a terms list that grows.** It becomes unusable past a short list. Use a Search List Box.
- **Using rich text to list related topics.** The API can't filter on text inside an HTML field.
- **Category pages built from hand-picked lists.** If a page should show "everything in this topic", query by the term, so new content appears without editing the page.

## Related

- [Tagging Content in Agility](/docs/developers/tagging-content-in-agility)
- [Nest, Link or Share? Choosing Linked Content Field Types](/docs/developers/linked-content-field-types)
- [Content Modeling Anti-Patterns](/docs/developers/content-modeling-anti-patterns)
- [GraphQL & Rest API Filtering](/docs/developers/graphql-operators)
- [Publishing to Multiple Destinations](/docs/overview/publishing-to-multiple-destinations)
