月ごとの記事一覧を作る
想定のパーリナイ。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)
みたいな階層構造にする必要がありそう。