Astroブログでタグページと関連記事を作るときの考え方
Astroで記事が増えてくると、タグ別ページと関連記事が欲しくなります。
どちらも、Markdownのfrontmatterに tags を持たせておくと作りやすいです。
frontmatterにtagsを持たせる
---
title: "記事タイトル"
pubDate: 2026-06-28
tags: ["Astro", "Markdown"]
---
Content Collectionsのschema側でも配列として定義しておきます。
tags: z.array(z.string()).default([])
schemaの設定は AstroのContent Collectionsでfrontmatterを型チェックする設定 に分けています。
タグ一覧を作る
記事一覧からタグを集計します。
const counts = new Map<string, number>();
for (const post of posts) {
for (const tag of post.data.tags) {
counts.set(tag, (counts.get(tag) ?? 0) + 1);
}
}
タグ名をURLにする場合は、slug化しておきます。
function getTagSlug(tag: string) {
return tag.toLowerCase().replace(/[^a-z0-9]+/g, '-');
}
C++ のようなタグは、そのままだとURLにしづらいので c-plus-plus のように変換すると扱いやすいです。
タグ別ページを作る
src/pages/tags/[tag].astro を作り、getStaticPaths() でタグごとのページを生成します。
---
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('posts', ({ data }) => !data.draft);
const tags = getTagSummaries(posts);
return tags.map((tag) => ({
params: { tag: tag.slug },
props: {
tag: tag.name,
posts: posts.filter((post) => post.data.tags.includes(tag.name)),
},
}));
}
---
動的ルーティングはAstro公式のRoutingにまとまっています。
https://docs.astro.build/en/guides/routing/#dynamic-routes
関連記事を出す
関連記事は、共有タグ数で並べるのが簡単です。
const relatedPosts = allPosts
.filter((candidate) => candidate.id !== post.id)
.map((candidate) => ({
post: candidate,
score: candidate.data.tags.filter((tag) => post.data.tags.includes(tag)).length,
}))
.filter(({ score }) => score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, 3)
.map(({ post }) => post);
完璧な推薦ではありませんが、技術ブログならまずこれで十分です。
まとめ
タグページと関連記事は、同じ tags から作れます。
- frontmatterに
tagsを持たせる - タグ名はURL用にslug化する
/tags/{tag}/をgetStaticPaths()で生成する- 関連記事は共有タグ数で並べる
記事が増える前にタグの粒度を決めておくと、後から整理しやすくなります。


