[Astro] 投稿記事のURLをファイルベースではなく指定の文字列にする
先日ブログサイト( https://chocolat5.com/ )を Next.js から Astro へ移行した際に、一番気掛かりだったのが「投稿記事のURLがマークダウンファイルのファイル名と違う」ことです。
Astro のルーティングはファイル名がURLになるファイルベース方式をとっていますが、移行したブログサイトでは以下のような構造とルールで管理していました。
- 記事内に画像がある場合は
フォルダ/index.md
、画像がない場合はマークダウンファイルのみ - ファイルまたはフォルダ名の先頭は投稿年月日
- 記事のURLはファイル名の年月日を除いた部分(例: 2024-02-24-post-1.md → /blog/post-1/)
/src
/content
/blog
/2024-02-24-post-1.md
/2024-02-27-post-2/index.md
/2024-05-14-post-3/index.md
/2024-07-24-post-4.md
ファイル・フォルダ名の年月日は、フォルダ内での投稿記事の見通しをよくするために付けています(フォルダ内で記事が日付順に並ぶ)。画像がない場合でも一律でフォルダにするとさらに統一されて見通しがよくなるのですが、今のところはこの形式を維持しています。
既存のファイル構造とURLをAstroへの移行後も維持するためには、それに合わせたURLとファイルへのパスを設定する必要がありました。
前提
- Astro 5.0
- マークダウンファイルで投稿記事を管理している
- Astro の Content collections を使用する
URL, ファイルパスの指定方法
slug と path を生成
URLとして使用される slug
、記事として表示するファイルまでのルートである path
をそれぞれ自分のサイトの構造と合わせたものを生成します。
移行したブログサイトを例にすると、各記事に対して必要な slug と path は以下になります。
ファイル名 | slug | path |
---|---|---|
2024-02-24-post-1.md | post-1 | 2024-02-24-post-1.md |
2024-02-27-post-2/index.md | post-2 | 2024-02-27-post-2/index.md |
getCollection()
で取得できるデータの中に filePath
というsrcから記事ファイルまでのルートパスがあるため、そこから slug
や path
を生成することも可能です。
※ slug と path は各サイトの構造に依存するため、具体的な生成方法はここでは省略します。
getStaticPaths で設定
生成した slug
と path
をそれぞれ投稿記事を表示する [slug].astro
の getStaticPaths()
内で params
として設定して、Astroのデフォルトの slug
と path
を上書きします。
---
export async function getStaticPaths() {
/* 全投稿記事を取得 */
const posts = await getCollection("blog");
return posts.map((post) => ({
/* ここでAstroのデフォルトの slug と path を上書きする */
params: { slug: [YOUR_SLUG], path: [YOUR_PATH] },
props: { post },
}));
}
const post = Astro.props;
const { Content } = await render(post);
---
<Content />
ブログサイトでは post
を受け取って { slug, path }
を返す getPostParam()
を作成して、getStaticPaths()
の return
内を以下のように変更しました。
export async function getStaticPaths() {
...
return posts.map((post) => ({
- params: { slug: [YOUR_SLUG], path: [YOUR_PATH] },
+ params: getPostParam(post),
props: { post },
}));
}
Discussion