Adding Topics (aka tags) in Astro

#tutorial

This month’s Virtual Coffee challenge is Build in Public. And I wanted to kick things off with some website updates. I’ve been meaning to add tags to my site for a long time. After poking around at other people’s sites, I decided to switch from the concept of tagging to the concept of topics. While the naming is different the process to adding topics was the same. In this post I want to share how I added topics, what I learnt, and what’s next.

How I added topics to my site

I mainly followed the Astro guide on building a tag index page.

Firstly, I created this structure in my project:

pages/
  topics/
    [topic].astro
    index.astro

index.astro is rendered when you go to /topics (example link).

[topic].astro is rendered when you go to /topics/:topic. Where :topic is something I define in my blog posts. Example, topics/api%20design/ (example link).

I then added the topics field to my front matter yml in my blog posts:

---
pubDate: 2023-04-05
updatedDate: 2023-04-05
title: "template"
description: "A template"
draft: true
topics: []
---

Added topics key in my config.ts file (source code)

topics: z
  .array(z.string())
  .optional()
  .default([])

Finally, to display the topics on my blog post, I made a component that is pulled into the Blog Post Layout:

BlogPost.astro

...
{topics && topics.length > 0 && <TopicList topics={topics} />}
...

TopicList.astro

---
const { topics } = Astro.props;
---

<p class="flex space-x-3">
{topics.map((topic: string) => (
  <a href={`/topics/${topic}`}>{topic}</a>
))}
</p>

Deep dive (aka what did I learn)

Digging into the index.astro and [topic].astro files a bit more.

index.astro

The way we get a list of topics to display on the /topic page is through this code (source code):

const posts = (await getCollection('blog'))
const topics = [...new Set(posts.flatMap((post) => post.data.topics))];

This line will get all my posts from my blog folder, pull out all of the topics, and return an array of topics with no duplicates.

[topic].astro

To generate the corresponding topic endpoints like /topic/api%20design/ I needed to modify the getStaticPaths() function in the [topic].astro. (source code). I forgot to modify it originally, and I was very confused when I kept getting a 404 not found error when trying to navigate to a topic page 🤦🏻!

export async function getStaticPaths() {
  const posts = (await getCollection('blog'))
  const topics = [...new Set(posts.flatMap((post) => post.data.topics))];
  return topics.map((topic) => ({
    params: { topic }
  }));
}

getStaticPaths() is an Astro function that Astro uses to generate each page at build time. It returns an array that looks something like this:

[
  { params: {topic: "API Design"} },
  { params: {topic: "Productivity"} }
]

What’s next

I’m not super happy with how my topics are styled. But I’m also not sure how I would style them - I’m open to suggestions! Next up for me is writing an About me page. I don’t know why but I find it incredibly hard to write! I’ll be chugging away at it while trying to come up with a better styling in the meantime.

Want to stay connected?

Subscribe to my newsletter

Weekly, bite-sized, practical tips and insights that have meaningfully improved how I code, design, and write.

No spam. Unsubscribe anytime.