Taxonomies
Taxonomies are classification systems for organizing content. EmDash includes built-in categories and tags, and supports custom taxonomies for specialized classification needs.
Built-in Taxonomies
Section titled “Built-in Taxonomies”EmDash provides two default taxonomies:
| Taxonomy | Type | Description |
|---|---|---|
| Categories | Hierarchical | Nested classification with parent-child relationships |
| Tags | Flat | Simple labels without hierarchy |
Both are available for the posts collection by default.
Managing Terms
Section titled “Managing Terms”Create a Term
Section titled “Create a Term”-
Go to the taxonomy page (e.g.,
/_emdash/admin/taxonomies/category) -
Enter the term name in the Add New form
-
Optionally set:
- Slug - URL identifier (auto-generated from name)
- Parent - For hierarchical taxonomies
- Description - Term description
-
Click Add
-
Open a content entry in the editor
-
Find the taxonomy panel in the sidebar
-
For categories: check the boxes for applicable terms, or click + Add New
-
For tags: type tag names separated by commas
-
Save the content
POST /_emdash/api/taxonomies/category/termsContent-Type: application/jsonAuthorization: Bearer YOUR_API_TOKEN
{ "slug": "tutorials", "label": "Tutorials", "parentId": "term_abc", "description": "How-to guides and tutorials"}Edit a Term
Section titled “Edit a Term”-
Go to the taxonomy terms page
-
Click Edit next to the term
-
Update the name, slug, parent, or description
-
Click Save
Delete a Term
Section titled “Delete a Term”-
Go to the taxonomy terms page
-
Click Delete next to the term
-
Confirm the deletion
Querying Taxonomies
Section titled “Querying Taxonomies”EmDash provides functions to query taxonomy terms and filter content by term.
Get All Terms
Section titled “Get All Terms”Retrieve all terms for a taxonomy:
import { getTaxonomyTerms } from "emdash";
// Get all categories (returns tree structure)const categories = await getTaxonomyTerms("category");
// Get all tags (returns flat list)const tags = await getTaxonomyTerms("tag");For hierarchical taxonomies, terms include a children array:
interface TaxonomyTerm { id: string; name: string; // Taxonomy name ("category") slug: string; // Term slug ("news") label: string; // Display label ("News") parentId?: string; description?: string; children: TaxonomyTerm[]; count?: number; // Number of entries with this term}Get a Single Term
Section titled “Get a Single Term”import { getTerm } from "emdash";
const category = await getTerm("category", "news");// Returns TaxonomyTerm or nullGet Terms for an Entry
Section titled “Get Terms for an Entry”import { getEntryTerms } from "emdash";
// Get all categories for a postconst categories = await getEntryTerms("posts", "post-123", "category");
// Get all tags for a postconst tags = await getEntryTerms("posts", "post-123", "tag");Filter Content by Term
Section titled “Filter Content by Term”Use getEmDashCollection with the where filter:
import { getEmDashCollection } from "emdash";
// Posts in the "news" categoryconst { entries: newsPosts } = await getEmDashCollection("posts", { status: "published", where: { category: "news" },});
// Posts with the "javascript" tagconst { entries: jsPosts } = await getEmDashCollection("posts", { status: "published", where: { tag: "javascript" },});Or use the convenience function:
import { getEntriesByTerm } from "emdash";
const newsPosts = await getEntriesByTerm("posts", "category", "news");Building Taxonomy Pages
Section titled “Building Taxonomy Pages”Category Archive
Section titled “Category Archive”Create a page that lists posts in a category:
---import { getTaxonomyTerms, getTerm, getEmDashCollection } from "emdash";import Base from "../../layouts/Base.astro";
export async function getStaticPaths() { const categories = await getTaxonomyTerms("category");
// Flatten hierarchical tree for routing function flatten(terms) { return terms.flatMap((term) => [term, ...flatten(term.children)]); }
return flatten(categories).map((cat) => ({ params: { slug: cat.slug }, props: { category: cat }, }));}
const { category } = Astro.props;
const { entries: posts } = await getEmDashCollection("posts", { status: "published", where: { category: category.slug },});---
<Base title={category.label}> <h1>{category.label}</h1> {category.description && <p>{category.description}</p>} <p>{category.count} posts</p>
<ul> {posts.map((post) => ( <li> <a href={`/blog/${post.data.slug}`}>{post.data.title}</a> </li> ))} </ul></Base>Tag Archive
Section titled “Tag Archive”Create a page that lists posts with a tag:
---import { getTaxonomyTerms, getEmDashCollection } from "emdash";import Base from "../../layouts/Base.astro";
export async function getStaticPaths() { const tags = await getTaxonomyTerms("tag");
return tags.map((tag) => ({ params: { slug: tag.slug }, props: { tag }, }));}
const { tag } = Astro.props;
const { entries: posts } = await getEmDashCollection("posts", { status: "published", where: { tag: tag.slug },});---
<Base title={`Posts tagged "${tag.label}"`}> <h1>#{tag.label}</h1>
<ul> {posts.map((post) => ( <li> <a href={`/blog/${post.data.slug}`}>{post.data.title}</a> </li> ))} </ul></Base>Category List Widget
Section titled “Category List Widget”Display a list of categories with post counts:
---import { getTaxonomyTerms } from "emdash";
const categories = await getTaxonomyTerms("category");---
<nav class="category-list"> <h3>Categories</h3> <ul> {categories.map((cat) => ( <li> <a href={`/category/${cat.slug}`}> {cat.label} ({cat.count}) </a> {cat.children.length > 0 && ( <ul> {cat.children.map((child) => ( <li> <a href={`/category/${child.slug}`}> {child.label} ({child.count}) </a> </li> ))} </ul> )} </li> ))} </ul></nav>Tag Cloud
Section titled “Tag Cloud”Display tags with size based on usage:
---import { getTaxonomyTerms } from "emdash";
const tags = await getTaxonomyTerms("tag");
// Calculate font sizes based on countconst counts = tags.map((t) => t.count ?? 0);const maxCount = Math.max(...counts, 1);const minSize = 0.8;const maxSize = 2;
function getSize(count: number) { const ratio = count / maxCount; return minSize + ratio * (maxSize - minSize);}---
<div class="tag-cloud"> {tags.map((tag) => ( <a href={`/tag/${tag.slug}`} style={`font-size: ${getSize(tag.count ?? 0)}rem`} > {tag.label} </a> ))}</div>Displaying Terms on Content
Section titled “Displaying Terms on Content”Show categories and tags on a post:
---import { getEntryTerms } from "emdash";
interface Props { collection: string; entryId: string;}
const { collection, entryId } = Astro.props;
const categories = await getEntryTerms(collection, entryId, "category");const tags = await getEntryTerms(collection, entryId, "tag");---
<div class="post-terms"> {categories.length > 0 && ( <div class="categories"> <span>Posted in:</span> {categories.map((cat, i) => ( <> {i > 0 && ", "} <a href={`/category/${cat.slug}`}>{cat.label}</a> </> ))} </div> )}
{tags.length > 0 && ( <div class="tags"> {tags.map((tag) => ( <a href={`/tag/${tag.slug}`} class="tag"> #{tag.label} </a> ))} </div> )}</div>Custom Taxonomies
Section titled “Custom Taxonomies”Create taxonomies beyond categories and tags for specialized needs.
Create a Custom Taxonomy
Section titled “Create a Custom Taxonomy”Use the admin API to create a taxonomy:
POST /_emdash/api/taxonomiesContent-Type: application/jsonAuthorization: Bearer YOUR_API_TOKEN
{ "name": "genre", "label": "Genres", "labelSingular": "Genre", "hierarchical": true, "collections": ["books", "movies"]}Use Custom Taxonomies
Section titled “Use Custom Taxonomies”Query and display custom taxonomies the same way as built-in ones:
import { getTaxonomyTerms, getEmDashCollection } from "emdash";
// Get all genresconst genres = await getTaxonomyTerms("genre");
// Get books in a genreconst { entries: sciFiBooks } = await getEmDashCollection("books", { where: { genre: "science-fiction" },});Assign to Collections
Section titled “Assign to Collections”Taxonomies specify which collections they apply to:
{ "name": "difficulty", "label": "Difficulty Levels", "hierarchical": false, "collections": ["recipes", "tutorials"]}Taxonomy API Reference
Section titled “Taxonomy API Reference”REST Endpoints
Section titled “REST Endpoints”| Endpoint | Method | Description |
|---|---|---|
/_emdash/api/taxonomies | GET | List taxonomy definitions |
/_emdash/api/taxonomies | POST | Create taxonomy |
/_emdash/api/taxonomies/:name/terms | GET | List terms |
/_emdash/api/taxonomies/:name/terms | POST | Create term |
/_emdash/api/taxonomies/:name/terms/:slug | GET | Get term |
/_emdash/api/taxonomies/:name/terms/:slug | PUT | Update term |
/_emdash/api/taxonomies/:name/terms/:slug | DELETE | Delete term |
Assign Terms to Content
Section titled “Assign Terms to Content”POST /_emdash/api/content/posts/post-123/terms/categoryContent-Type: application/jsonAuthorization: Bearer YOUR_API_TOKEN
{ "termIds": ["term_news", "term_featured"]}Next Steps
Section titled “Next Steps”- Create a Blog - Use categories and tags in a blog
- Querying Content - Filter by taxonomy terms
- Working with Content - Assign terms in the editor