ブログの記事数が増えてくると、一覧ページが長くなってしまいます。ページネーションを導入すれば、ページを分割して表示でき、ユーザー体験が向上します。ここでは、Astroの paginate 機能を使って、記事一覧ページに1ページあたり5件表示のページネーションを実装する手順を紹介します。
ページネーション実装手順
1.動的ルートファイルを作成 (src/pages/blog/[...page].astro)**
- もし既に記事一覧ページがあれば、そのファイル(例えば
src/pages/blog/index.astro)を 削除 するか、別の名前に変更してください。(例:index-old.astro) - 次に、
src/pages/blog/ディレクトリ内に[...page].astroという名前で 新しいファイルを作成 します。このファイルが/blog/,/blog/2,/blog/3, ... といったページをすべて処理します。
your-project/
└── src/
├── content/
│ └── blog/
├── pages/
│ ├── blog/
│ │ └── [...page].astro <-- これを新規作成
│ ├── tags/
│ └── [...slug].astro (個別記事ページ)
└── layouts/
2.getStaticPaths でページ分割を定義
作成した src/pages/blog/[...page].astro ファイルを開き、フロントマター部分 (--- 内) に getStaticPaths 関数を実装します。ここでAstroの paginate 関数を使ってページ分割を行います。
---
// src/pages/blog/[...page].astro
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro'; // 適切なレイアウトを指定
export async function getStaticPaths({ paginate }) {
// 1. 'blog'コレクションの記事をすべて取得
const allPosts = await getCollection('blog');
// 2. 公開日 (pubDate) の新しい順に記事をソート (ページ分割する前にソートする)
const sortedPosts = allPosts.sort((a, b) =>
b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
// 3. paginate関数でページ分割 (1ページあたり5件)
// ソート済みの記事リストを渡す
return paginate(sortedPosts, { pageSize: 5 });
}
// paginateから渡された現在のページ情報を受け取る
const { page } = Astro.props;
// 個別記事ページへのURLを生成するヘルパー関数 (他ページで使っているものを流用)
function getPostUrl(post) {
if (post.data.url) {
return `/${post.data.url}/`;
}
return `/blog/${post.slug}/`;
}
---
-
getStaticPathsは引数として{ paginate }を受け取ります。 -
getCollection('blog')で全記事を取得し、必要に応じてsort()で並び替えます(重要:paginateする前にソートしてください)。 -
paginate(sortedPosts, { pageSize: 5 })を呼び出します。第1引数に記事データの配列、第2引数のpageSizeに1ページあたりの表示件数を指定します。これが各ページのパスとデータを自動生成してくれます。 -
const { page } = Astro.props;で、現在のページに関する情報(記事データ、URLなど)を受け取ります。
3.ページネーションされた記事リストの表示
[...page].astro のHTMLテンプレート部分で、page.data(現在のページの記事リスト)を使って記事を表示します。
---
// --- 上記のフロントマターコード ---
---
<BaseLayout title={`ブログ記事一覧 - ${page.currentPage}ページ目`}>
<h1>ブログ記事一覧</h1>
<p>{page.start + 1}件目から{page.end + 1}件目を表示 ({page.total}件中)</p>
<ul>
{/* page.data に現在のページの5件分の記事が入っている */}
{page.data.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>
</BaseLayout>
-
Astro.propsから受け取ったpageオブジェクトを使います。 -
page.dataが現在のページに表示すべき記事(最大5件)の配列なので、これをmap()でループして表示します。 -
page.currentPage(現在のページ番号)、page.total(全記事数) なども表示に利用できます。 - 個別記事へのリンクは、これまで通り
getPostUrlヘルパー関数などで生成します。
4.ページネーションリンクの表示
記事リストの下などに、「前へ」「次へ」のナビゲーションリンクを追加します。page.url.prev と page.url.next を使います。
---
// --- 上記のフロントマターコード & 記事リスト表示コード ---
---
<BaseLayout title={`ブログ記事一覧 - ${page.currentPage}ページ目`}>
{/* ... 記事リスト ... */}
{/* ↓ ここからページネーションリンクを追加 */}
<nav aria-label="ページネーション">
{/* 前のページへのリンク (page.url.prevが存在する場合のみ表示) */}
{page.url.prev ? (
<a href={page.url.prev}>← 前のページへ</a>
) : (
<span>← 前のページへ</span> // リンク無効表示 (任意)
)}
<span style="margin: 0 1em;">
{page.currentPage} / {page.lastPage} ページ
</span>
{/* 次のページへのリンク (page.url.nextが存在する場合のみ表示) */}
{page.url.next ? (
<a href={page.url.next}>次のページへ →</a>
) : (
<span>次のページへ →</span> // リンク無効表示 (任意)
)}
</nav>
{/* ↑ ここまでページネーションリンク */}
</BaseLayout>
-
page.url.prevが存在すれば「前のページへ」のリンクを表示します。最初のページではundefinedになります。 -
page.url.nextが存在すれば「次のページへ」のリンクを表示します。最後のページではundefinedになります。 - 現在のページ番号 (
page.currentPage) と総ページ数 (page.lastPage) を表示すると分かりやすいです。
開発サーバーで確認
ターミナルで開発サーバーを起動します。
npm run dev
# または pnpm dev, yarn dev
ブラウザで以下のURLにアクセスして確認してみましょう。
-
1ページ目:
http://localhost:4321/blog/ -
2ページ目:
http://localhost:4321/blog/2 -
3ページ目:
http://localhost:4321/blog/3
各ページに記事が5件ずつ表示され、「前へ」「次へ」のリンクが正しく機能し、最後のページでは「次へ」が、最初のページでは「前へ」がリンク切れ(または非表示)になっていれば完了です。