ブログの記事数が増えてくると、一覧ページが長くなってしまいます。ページネーションを導入すれば、ページを分割して表示でき、ユーザー体験が向上します。ここでは、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件ずつ表示され、「前へ」「次へ」のリンクが正しく機能し、最後のページでは「次へ」が、最初のページでは「前へ」がリンク切れ(または非表示)になっていれば完了です。