haihai Blog

Astro フレームワークのタグページを動的に生成する

2024-07-09

はじめに

Astro フレームワークの投稿のタグを所得し、タグを利用した投稿のページ一覧を生成します。

動的ページな tag ページを作成する

tag ごとのページを動的に作成します。
tag ページには、tag が付いている投稿一覧を表示します。 src/pages/tags/[tag].astro に新規ファイルを作成します。ファイル名に [] が含まれていることに注意してください。

---
// src/pages/tags/[tag].astro
import Layout from "~/layouts/Layout.astro";
import Header from "~/components/Header.astro";
import Footer from "~/components/Footer.astro";

import { getCollection } from "astro:content";
export async function getStaticPaths() {
  const posts = await getCollection("posts");
  // Get all unique tags
  const uniqueTags = [...new Set(posts.map((post) => post.data.tags).flat())];
  return uniqueTags.map((tag) => {
    // Filter posts by tag
    const filteredPosts = posts.filter((post) => post.data.tags.includes(tag));
    return {
      params: { tag },
      props: { posts: filteredPosts },
    };
  });
}

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

<Layout title=`Tag: ${tag}`>
  <Header />
  <main>
    <section>
      <h1 class="large-tag">{tag}</h1>
      <p>タグに関連する投稿一覧です。</p>
    </section>
    <section>
      <h2>Posts</h2>
      <ul>
        {
          posts.map((post) => (
            <li>
              <a href={`/posts/${post.slug}`}>{post.data.title}</a>
            </li>
          ))
        }
      </ul>
    </section>
  </main>
  <Footer />
</Layout>

getStaticPaths() で返却する params へ動的に生成するページを配列で設定します。
ファイル名に動的パラメータを使用する場合、そのコンポーネントは getStaticPaths() 関数をエクスポートする必要があります。Astro は、 SSG のため、ビルド時に生成するページを指定する必要があるためのようです。
生成されたそれぞれの tag ページで利用する tag を利用している投稿を getStaticPaths() で返却する props へ設定します。

getCollection("posts") で投稿を全て取得し、[...new Set(posts.map((post) => post.data.tags).flat())] で投稿に含まれる tag の重複を削除しています。

タグ一覧ページを作成する

tag 一覧を作成します。 tag には、それぞれの tag ページへのリンクをします。
src/pages/tags/index.astro を作成します。

---
// src/pages/tags/index.astro
import Layout from "~/layouts/Layout.astro";
import Header from "~/components/Header.astro";
import Footer from "~/components/Footer.astro";

import { getCollection } from "astro:content";
const posts = await getCollection("posts");
const tags = [...new Set(posts.map((post) => post.data.tags).flat())];

const title = "Tags";
---

<Layout title={title}>
  <Header />
  <main>
    <section>
      <h1>{title}</h1>
      <p>タグ一覧です。</p>
    </section>
    <section>
      <div class="flex flex-wrap mt-4">
        {
          tags.map((tag) => (
            <span class="tag mr-2 my-1">
              <a href={`/tags/${tag}`}>{tag}</a>
            </span>
          ))
        }
      </div>
    </section>
  </main>
  <Footer />
</Layout>

タグのスタイルを設定する

base.css へ追記します。

.large-tag {
  @apply bg-blue-500 text-white rounded-3xl px-6 py-2 text-2xl font-bold shadow-lg inline-block;
}
.tag {
  @apply bg-blue-500 text-white rounded-lg px-3 py-1 text-sm font-semibold shadow-md inline-block;
}

参考 URL