Adding Tags

Adding Tags

Tags in this post:

Astro already has a tutorial on this, and after checking that out I’m pretty much going to copy and paste and make some tailwind changes.

The first step is to just add tags to the front matter of all of your posts. Thankfully, I only have 2 at this point! 🤣

tags: ["astrojs", "webdev", "view-transitions"];

Now after reviewing the tutorial on Astro’s site, I think I’ll skip straight to automatically reading tags from my content files. So we’ll start with pages/tags/index.astro:

---
---
// /src/pages/tags/index.astro

import Layout from "../../layouts/DefaultLayout.astro";
import { getCollection } from "astro:content";

const allPosts = await getCollection("blog");

const tags = {};
allPosts.map((post) => {
  post.data.tags.map((tag) => {
    if (tags[tag]) {
      tags[tag]++;
    } else {
      tags[tag] = 1;
    }
  });
});
---

<Layout title="tags">
  <main class="content">
    <h1 class="text-2xl font-bold mb-4">Tags</h1>
    <ul class="grid grid-cols-2 md:grid-cols-4 gap-4">
      {
        Object.keys(tags).map((tag) => (
          <li>
            <a
              href={**/tags/${tag}**}
              class="text-xl p-2 block rounded-md shadow-md text-center relative text-nowrap dark:bg-neutral-950 dark:text-neutral-200 bg-neutral-200 hover:bg-neutral-100 transition-colors duration-200 ease-in-out hover:dark:bg-black hover:dark:text-white"
            >
              {tag}
              <b class="rounded-full text-xs absolute -top-1 -right-1 w-5 h-5 flext items-center justify-around dark:bg-white dark:text-black dark:border-neutral-800 bg-neutral-400 text-black">
                {tags[tag]}
              </b>
            </a>
          </li>
        ))
      }
    </ul>
  </main>
</Layout>

Now, on the next page, I’m going to want to list blog entries again, so I’m going to abstract that from pages/blog/index.astro into it’s own component:

---
// /src/components/BlogPostListing.astro
import { type BlogPost } from "../content/config";
interface Props {
  post: BlogPost;
}

const { post } = Astro.props as Props;

let heroHtml = "";
if (post.data.heroImage && post.data.heroImageDark) {
  heroHtml = `
    <img src="${post.data.heroImage}" class="dark:hidden w-full h-full" transition:name="hero-${post.slug}" transition:animate={fade({duration:'2s'})}></img>
    <img src="${post.data.heroImageDark}" class="dark:block hidden w-full h-full" transition:name="hero-${post.slug}-dark" transition:animate={fade({duration:'2s'})}></img>
  `;
} else if (post.data.heroImage) {
  heroHtml = `
  <img src="${post.data.heroImage}" class="w-full h-40" transition:name="hero-${post.slug}" transition:animate={fade({duration:'2s'})}></img>
  `;
}
---

<a
  href={**/blog/${post.slug}**}
  class="flex flex-col space-y-2 shadow-md border border-neutral-300 dark:border-neutral-800 rounded-md bg-neutral-200 dark:bg-neutral-900"
>
  <div set:html={heroHtml} />
  <div class="px-4 pb-4">
    <h5 class="text-lg font-bold">{post.data.title}</h5>
    <p class="prose flex-grow">{post.data.description}</p>
  </div>
</a>

Now I can create tags/[tag].astro:

---
// /src/tags/[tag].astro
import { getCollection } from "astro:content";
import Layout from "../../layouts/DefaultLayout.astro";
import BlogPostCard from "../../components/BlogPostCard.astro";

export async function getStaticPaths() {
  const allPosts = await getCollection("blog");
  const uniqueTags = [
    ...new Set(allPosts.map((post) => post.data.tags).flat()),
  ];

  return uniqueTags.map((tag) => {
    const filteredPosts = allPosts.filter((post) =>
      post.data.tags.includes(tag)
    );
    return {
      params: { tag },
      props: { posts: filteredPosts },
    };
  });
}

const { tag } = Astro.params;
const { posts } = Astro.props;
---

<Layout pageTitle={tag}
  ><main class="content">
    <h2 class="text-2xl font-bold mb-4">Tag: {tag}</h2>
    {
      !posts.length && (
        <p>
          There are no posts tagged with "<b>{tag}</b>"
        </p>
      )
    }
    {
      posts.length > 0 && (
        <div>
          <p class="mb-4">
            Found {posts.length} post{posts.length == 1 ? "" : "s"} tagged with{" "}
            {tag}
          </p>
          <ul class="grid grid-cos-1 sm:grid-cols-2 gap-4">
            {posts.map((post) => (
              <BlogPostCard post={post} />
            ))}
          </ul>
        </div>
      )
    }
  </main>
</Layout>

ThomPorter.com