Categories
What Are Categories?
Section titled “What Are Categories?”Categories are broad groupings attached to blog posts. Where tags are specific keywords, categories are high-level sections — tutorial, news, guides, contact.
Every category you add to a post frontmatter automatically creates a category archive page at /categories/<category>/. That page lists every post in the category. No manual configuration needed.
Categories and tags work side by side. A post usually has one or two categories and several tags.
Where Categories Live
Section titled “Where Categories Live”Categories are not stored in a separate file. They live inside the frontmatter of every blog post:
categories: ["contact"]The category system reads every blog post, collects all categories, and builds:
- A category index page at
/categories/— lists every category in use - A category archive page at
/categories/<category>/— lists every post in that category
Both are generated automatically.
How the Category Index Works
Section titled “How the Category Index Works”The category index lives at src/pages/categories/index.astro:
---import BaseLayout from "../../design/base.astro";import Data from "../../data/configuration/blog/blogpage.json";
const allPosts = Object.values(import.meta.glob("../blog/*.md", { eager: true })) as any[];const categories = [...new Set(allPosts.map((blog) => blog.frontmatter.categories).flat().filter(Boolean))];const pageTitle = "Category Index";const canonicalURL = new URL(Astro.url.pathname, Astro.site);---<BaseLayout pageTitle={pageTitle}> <div class="col-md-12 p-3 text-center"> <h1><strong><a href={canonicalURL}>{Data.categories_page_title}</a></strong></h1> <h2>{Data.categories_page_description}</h2> </div> <div class="row mt-5"> {categories.map((category: any) => ( <h3 class="col-md-4 p-3"><a href={`/categories/${category}`}>{category}</a></h3> ))} </div></BaseLayout>What it does
Section titled “What it does”- Loads every blog post —
import.meta.glob("../blog/*.md", { eager: true })reads all.mdfiles insrc/pages/blog/at build time. - Extracts all categories — maps over every post, takes
frontmatter.categories, flattens the arrays, and filters out empty values. - Deduplicates —
new Set(...)removes duplicates so each category appears once. - Builds a canonical URL —
new URL(Astro.url.pathname, Astro.site)produces an absolute URL for the current page. - Renders the list — loops through unique categories and displays each as a link to
/categories/<category>/. - Reads page title and description — from
blogpage.json(categories_page_titleandcategories_page_description).
The result
Section titled “The result”A simple page listing every category used across your blog. Clicking any category goes to that category’s archive.
How a Category Archive Works
Section titled “How a Category Archive Works”Each category has its own page generated dynamically by src/pages/categories/[category].astro:
---import BaseLayout from "../../design/base.astro";import BlogPost from "../../widget/BlogPost.astro";
export async function getStaticPaths() { const allPosts = Object.values(import.meta.glob("../blog/*.md", { eager: true })) as any[]; const uniqueCategories = [...new Set(allPosts.map((post) => post.frontmatter.categories).flat())]; return uniqueCategories.map((category) => ({ params: { category }, props: { posts: allPosts.filter((post) => post.frontmatter.categories?.includes(category)) }, }));}
const { category } = Astro.params;const { posts } = Astro.props as any;---<BaseLayout> <div class="col-md-12 p-3 text-center"> <h1>{category}</h1> <h2 class="lead">List of categories for {category}</h2> </div> {posts.map((post: any) => ( <BlogPost url={post.url} title={post.frontmatter.title} description={post.frontmatter.description} image={post.frontmatter.image} /> ))}</BaseLayout>What it does
Section titled “What it does”getStaticPaths()— runs at build time. It reads every blog post, collects unique categories, and generates one page per category.- For each category, it filters all posts to find only those that include the category.
Astro.params.category— the current category being rendered (from the URL).Astro.props.posts— the list of posts for this category.- Renders each post using the
<BlogPost />widget.
The result
Section titled “The result”Each category gets its own page at /categories/<category>/, listing all posts in that category. For example, /categories/contact/ lists every post in the contact category.
Adding Categories to a Post
Section titled “Adding Categories to a Post”Categories are added in the frontmatter of any blog post:
---layout: ../../design/blog/blogpost.astrotitle: "My Post"description: "Short summary"image: /src/assets/cover.pngpubDate: 2025-01-15tags: ["astro", "tutorial"]categories: ["guides"]---The categories array can contain one or more categories. Once saved, the category archive pages for guides are regenerated automatically at the next build.
Rules for categories
Section titled “Rules for categories”- Lowercase —
guides, notGuides - Broad, one-word —
tutorial,news,contact - Consistent spelling — if you use
guidesin one post, do not useguideorGuidesin another - Few per post — one or two is typical. More than three dilutes the meaning
- No duplicates —
["guides", "guides"]is redundant
Editing the Category Pages
Section titled “Editing the Category Pages”Change the category index heading
Section titled “Change the category index heading”The category index reads from src/data/configuration/blog/blogpage.json:
"categories_page_title": "Categories Astro Themes","categories_page_description": "Update blog Docus Astro JS Starlight Themes template with complete - categories"Edit those values to change the title and description on /categories/.
Change the category archive layout
Section titled “Change the category archive layout”The category archive uses src/design/base.astro as its layout. Edit that file to change how individual category pages look.
Change the category archive post rendering
Section titled “Change the category archive post rendering”Each post in a category archive is rendered by <BlogPost /> from src/widget/BlogPost.astro. Edit that widget to change how posts appear in the list.
How Categories Differ From Tags
Section titled “How Categories Differ From Tags”| Categories | Tags |
|---|---|
| Broad | Specific |
| Few per post | Many per post |
tutorial, news | astro, contact form, seo |
| High-level grouping | Fine-grained discovery |
URL: /categories/<category>/ | URL: /tags/<tag>/ |
You can use both on the same post. A post about setting up a contact form might have:
tags: ["astro", "contact form"]categories: ["tutorial"]Categories help readers browse by section. Tags help readers find related content.
Rules to Remember
Section titled “Rules to Remember”- Categories live in post frontmatter — no separate config file.
- The category index and archives are auto-generated — no manual listing.
- Keep spelling consistent — inconsistent categories create duplicate archives.
- Use lowercase — for cleaner URLs and sorting.
- Do not rename categories lightly — renaming a category creates a new archive and breaks existing links.
- Empty category arrays are fine — posts without categories simply do not appear in any category archive.
Quick Recap
Section titled “Quick Recap”Blog post frontmatter: categories: ["guides", "tutorial"] │ ▼/categories/ → index of all categories/categories/guides/ → all posts in "guides"/categories/tutorial/ → all posts in "tutorial"Files involved:
| File | Purpose |
|---|---|
src/pages/categories/index.astro | Category index page |
src/pages/categories/[category].astro | Individual category archive |
src/design/base.astro | Category archive layout |
src/widget/BlogPost.astro | Post card in archives |
src/data/configuration/blog/blogpage.json | Category index title/description |
Add a category — edit a post’s frontmatter. That’s it. Everything else is automatic.