月ごとの記事一覧を作る


想定のパーリナイ。DE-TEIUです。 今日は、Astro製のブログで月ごとの記事一覧を作る方法を紹介します。

はてなブログとかにある、月別アーカイブみたいなやつです。

下準備

何かしら日付を加工できるライブラリを入れておきます。私はTempoを使いますがお好みで。

$ npm install @formkit/tempo

月ごとの記事一覧を作る

月ごとのページ一覧を作るには、年と月をキーにして記事を取得する必要があります。 ということで、src/pages/archive/[year]/[month].astroというパスで記事一覧を作成します。

( /archive/2025/01/ みたいなパスでアクセスする想定です )

---
import { type CollectionEntry, getCollection } from "astro:content";
import { format } from "@formkit/tempo";
import ArticleList from "../../../components/ArticleList.astro";

export async function getStaticPaths() {
  const posts = await getCollection("blog");

  // 月別に記事をまとめる
  const postsByMonth = posts.reduce(
    (items: any, post: CollectionEntry<"blog">) => {
      const key = format(post.data.pubDate, "YYYY/MM");

      if (!items[key]) {
        items[key] = [];
      }
      items[key].push(post);
      return items;
    },
    {}
  );

  // パスの一覧を作成
  const results = Object.keys(postsByMonth).map((key) => {
    const [year, month] = key.split("/");
    return {
      params: { year, month },
      props: postsByMonth[key],
    };
  });
  return results;
}

// 記事を取り出して日付順に並び替える
const posts = Object.values(Astro.props).sort(
  (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
---

<ArticleList posts={posts} />

これだけ。シンプルですね。 年月をキーにして記事を別々のリストに振り分けて、それをページに表示するだけです。

フッターに月別アーカイブのリンクを表示する

フッターに月別アーカイブのリンクを表示するには、フッターのコンポーネントに以下のコードを追加します。

---
import { getCollection } from "astro:content";
import { format } from "@formkit/tempo";
const today = new Date();

const posts = await getCollection("blog");
// 記事の公開日を年月の形式に変換し配列にする
const postsYearMonths = posts.map((post) => {
  return format(post.data.pubDate, "YYYY年MM月");
});
// 重複を削除
const distinctPostsYearMonths = Array.from(new Set(postsYearMonths));
// 記事の個数を数えて年月の後ろに付ける
const countedPostsYearMonths = distinctPostsYearMonths.map((item) => {
  return {
    yearMonth: item,
    yearMonthNum: Number(item.replace("年", "").replace("月", "")),
    count: postsYearMonths.filter((postYearMonth) => postYearMonth === item)
      .length,
  };
});

// 年月の新しい順でソート
const sortedCountedPostsYearMonths = countedPostsYearMonths.sort((a, b) => {
  return b.yearMonthNum - a.yearMonthNum;
});

HTML部分はこんな感じ。

<div>
  <h5 class="text-lg font-bold">月別アーカイブ</h5>
  <ul class="list-none ps-2 m-0">
    {
      sortedCountedPostsYearMonths.map((item) => (
        <li>
          <a
            href={`/archive/${item.yearMonth.replace("年", "/").replace("月", "")}/`}
            class="block py-1"
          >
            {item.yearMonth} ({item.count})
          </a>
        </li>
      ))
    }
  </ul>
</div>

これで

みたいなリンクが表示されます。

余談

今は記事数が少ないので、シンプルに年月をそのまま並べて表示していますが、記事数が増えてくると

  • 2025年(2147483647)
    • 12月(1)
    • 11月(2147483640)
    • 01月(6)
  • 2024年(8)
    • 12月(8)

みたいな階層構造にする必要がありそう。