Astro タグ別記事一覧ページの作り方

ここでは、「Content Collections API」とMarkdownの `tags` フロントマターを使って、タグごとの記事一覧ページを作成する手順を紹介します。

**前提:**

* Content Collections APIを使ってブログ記事 (`blog` コレクション) を管理していること。
* 各記事のMarkdownファイルのフロントマターに `tags` 配列が設定されていること (例: `tags: ["Astro", "初心者"]`)。

## タグ別記事一覧ページの作成手順

### 1.動的ルートファイルの作成 (`src/pages/tags/[tag].astro`)

まず、タグごとのページを生成するための動的ルートファイルを作成します。`src/pages/tags/` ディレクトリ（なければ作成）に `[tag].astro` という名前でファイルを作成します。`[tag]` の部分がURLのタグ名になります。

```
your-project/
└── src/
    ├── content/
    │   └── blog/
    ├── pages/
    │   ├── blog/
    │   ├── tags/         <-- このディレクトリを作成
    │   │   └── [tag].astro <-- このファイルを作成
    │   └── [...slug].astro (個別記事ページ)
    └── layouts/
```

### 2.`getStaticPaths` の実装 (タグと記事リストの生成)

作成した `src/pages/tags/[tag].astro` ファイルを開き、`getStaticPaths` 関数を実装します。この関数は、存在するすべてのタグに対してページを生成するようにAstroに指示し、各ページに必要なデータを渡します。

```astro
---
// src/pages/tags/[tag].astro
import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro'; // 適切なレイアウトを指定

export async function getStaticPaths() {
  const allPosts = await getCollection('blog');

  // 1. 全記事からユニークなタグを集める
  const uniqueTags = new Set<string>();
  allPosts.forEach((post) => {
    // post.data.tags が存在する場合のみ処理 (?. オプショナルチェイニング)
    post.data.tags?.forEach((tag) => {
      uniqueTags.add(tag); // Setが自動で重複を除外
    });
  });

  // 2. 各タグに対応するページ情報を作成
  return Array.from(uniqueTags).map((tag) => {
    // 3. このタグが含まれる記事だけをフィルタリング
    const filteredPosts = allPosts.filter((post) =>
      post.data.tags?.includes(tag)
    );
    // 4. ページのパスと渡すデータを返す
    return {
      params: { tag: tag }, // URLの [tag] 部分になる値
      props: {
        tag: tag, // ページコンポーネントで使うタグ名
        posts: filteredPosts, // このタグの記事リスト
      },
    };
  });
}

// getStaticPathsから渡されたデータを受け取る
const { tag, posts } = Astro.props;
// 記事を日付順にソート (任意)
posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

// URLを決定するヘルパー関数 (個別記事ページへのリンク用、前の手順から流用)
function getPostUrl(post) {
  if (post.data.url) {
    return `/${post.data.url}/`;
  }
  return `/blog/${post.slug}/`;
}
---
```
* `getCollection('blog')` で全記事を取得します。
* `Set` を使って、すべての記事からユニークなタグを集めます。`post.data.tags?.forEach` の `?.` で `tags` がない記事でもエラーにならないようにします。
* 集めたユニークなタグ (`uniqueTags`) それぞれについて、`map` で処理します。
* `filter` を使って、そのタグが含まれる記事 (`filteredPosts`) を抽出します。
* `params: { tag }` でページのURL (例: `/tags/Astro/`) を定義し、`props: { tag, posts }` でページコンポーネントにタグ名とそのタグの記事リストを渡します。

### 3.タグ別記事リストの表示

 `getStaticPaths` の下に、ページのHTMLテンプレート部分を記述します。`Astro.props` から渡された `tag` と `posts` を使って一覧を表示します。

```astro
---
// --- 上記の getStaticPaths と props の受け取りコード ---
---
<BaseLayout title={`タグ: ${tag}`}>
  <h1>タグ: 「{tag}」 の記事一覧</h1>

  <ul>
    {posts.map((post) => (
      <li>
        <a href={getPostUrl(post)}>{post.data.title}</a>
        <span style="font-size: 0.8em; margin-left: 8px;">
          ({post.data.pubDate.toLocaleDateString()})
        </span>
      </li>
    ))}
  </ul>

  <p>
    <a href="/tags/">すべてのタグ一覧へ</a>
  </p>
</BaseLayout>
```
* `Astro.props` から `tag` と `posts` を受け取ります。
* 受け取った `posts` 配列を `map` でループ処理し、各記事へのリンクとタイトルなどを表示します。記事へのリンクは、前の手順で定義した `getPostUrl` ヘルパー関数を使うと便利です。
* ページ全体を適切なレイアウトコンポーネント (`<BaseLayout>` など) で囲みます。

### 4.全タグ一覧ページの作成 (`src/pages/tags/index.astro`)

すべてのタグをリスト表示するページもあると便利です。`src/pages/tags/index.astro` を作成します。

```astro
---
// src/pages/tags/index.astro
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro'; // 適切なレイアウトを指定

const allPosts = await getCollection('blog');
const uniqueTags = new Set<string>();
allPosts.forEach((post) => {
  post.data.tags?.forEach((tag) => {
    uniqueTags.add(tag);
  });
});
const sortedTags = Array.from(uniqueTags).sort(); // アルファベット順にソート (任意)
---
<BaseLayout title="すべてのタグ">
  <h1>すべてのタグ一覧</h1>
  <ul>
    {sortedTags.map((tag) => (
      <li>
        <a href={`/tags/${tag}/`}>{tag}</a>
      </li>
    ))}
  </ul>
</BaseLayout>
```
* タグ別ページと同様に全記事からユニークなタグを集めます。
* 集めたタグを `map` でループし、各タグのタグ別一覧ページ (`/tags/タグ名/`) へのリンクを生成します。

### 5.開発サーバーで確認

ターミナルで開発サーバーを起動します。

```bash
npm run dev
# または pnpm dev, yarn dev
```

ブラウザで以下のURLにアクセスして確認してみましょう。

* **全タグ一覧ページ:** `http://localhost:4321/tags/`
* **タグ別記事一覧ページ:** `http://localhost:4321/tags/Astro/` (例: `Astro` タグのページ)

全タグ一覧ページが表示され、各タグをクリックするとそのタグが付いた記事の一覧ページに移動できれば成功です！