🧠 概要:
記事の概要
この記事では、WordPressとAstro.jsを利用してヘッドレスCMSの構築過程における「ページネーション」の実装方法について詳しく説明しています。具体的には、静的なページを生成し、取得したデータを使って複数のページに分けてコンテンツを表示する方法を示しています。
要約の箇条書き
- 実装目標: ページネーションを実装し、静的ページとして出力する。
- URL形式:
/tags/[slug]/page/[page].astro
。 - WordPress APIの使用: タグのIDとページ番号を使って、APIからデータを取得する。
- ページ数の計算:
getStaticPaths()
を使って必要な静的ページを計算・生成。 - ファイル構成:
/pages/tags/[slug]/page/[page].astro
という動的ルーティング構成。 - コード実装:
getStaticPaths()
内で、受け取ったタグからページ数を計算。- APIから投稿を取得し、エラー処理も実装。
- HTML実装: 動的に生成したページで投稿の内容やナビゲーションを表示。
- ページ番号のナビゲーション: 各ページに前後のリンクと現在のページ番号を表示する方法を提案。
- ビルド結果: 正常にページネーションが動作することを確認。
このプロセスを通じて、Astro.jsの特性とWordPress APIとの連携がどのように活用されるかが解説されています。
今回は、ページネーションを実装してみます。
ページネーションとコンテンツの表示部分だけ、Vueで組んでしまったりも出来ますが(Vue や React も利用できるのが Astro のいいとこですよね)、今回はがっつりと静的ページとして吐き出すようにします!
ページネーション実装の概要
-
URL形式:/tags/[slug]/page/[page].astro
-
WordPress APIでページ指定:?tags=ID&page=2&per_page=4
-
ページ数を計算して、getStaticPaths() で静的ページを複数生成
と、こんな感じになります!
ファイル構成
今回は /pages/tags/ の下に、タグとページ番号の2段階の動的ルーティングになる構成です。
src/pages/tags/[slug]/page/[page].astro
コード実装
そして実際のコードは、まずはスクリプトは
---
export async function getStaticPaths() {
const tagRes = await fetch('http://headless.local/wp-json/wp/v2/tags');
const tags = await tagRes.json();
const paths = [];
for (const tag of tags) {
const count = tag.count;
const totalPages = Math.ceil(count / 4);
for (let page = 1; page params: {
slug: tag.slug,
page: page.toString(),
},
props: {
tag,
currentPage: page,
},
});
}
}
return paths;
}
const { tag, currentPage } = Astro.props;
const offsetRes = await fetch(`http://headless.local/wp-json/wp/v2/posts?tags=${tag.id}&per_page=4&page=${currentPage}&_embed`);
let posts = [];
let fetchError = false;
try {
if (!offsetRes.ok) throw new Error(`記事取得失敗: ${offsetRes.status}`);
posts = await offsetRes.json();
} catch (err) {
console.error("記事取得エラー:", err);
fetchError = true;
}
---
投稿を取得する部分は、これまでやってきた実装と変わらないです。
今回の getStaticPaths() の中身を詳しく解説
ちと前にも getStaticPaths() 内の props の話しをしたんですが、今回は中身を細かく説明してみます!
params や props が何なのかが分かると思います。
const tagRes = await fetch('http:
const tags = await tagRes.json();
const paths = [];
この最初の3行は分かるとは思いますが…
const paths = [];
はこの後、for文で値を入れていく変数で、初期値として空の配列を入れています。
for (const tag of tags) {
const count = tag.count;
const totalPages = Math.ceil(count / 4);
fetchしてきたデータを tagRes.json() で
for (let page = 1; page
page という変数を 1 から totalPages(そのタグに必要なページ数)まで繰り返します。
例:投稿が 9 件で 1ページ4件 → totalPages = 3 → page = 1, 2, 3
paths.push({
params: {
slug: tag.slug,
page: page.toString(),
},
props: {
tag,
currentPage: page,
},
});
paths.push({
paths というリストに、1つずつ「ビルドすべきページの情報」を追加します。
params: {
slug: tag.slug,
page: page.toString(),
},
params は URL の /tags/[slug]/page/[page]/ に対応しています。
たとえば:
のように、何のパラメーターにどの値が入るかをここで指定します。
props: {
tag,
currentPage: page,
},
各ページに「そのタグ」と「いま何ページ目か」の情報をprops で渡します。
そして Astro.props 経由で .astro ファイル内で使えるようになります。
実行結果のイメージ
たとえば tag.slug = ‘travel’, 投稿数 9件(1ページ4件)なら:
[
{
params: { slug: 'travel', page: '1' },
props: { tag: {...}, currentPage: 1 }
},
{
params: { slug: 'travel', page: '2' },
props: { tag: {...}, currentPage: 2 }
},
{
params: { slug: 'travel', page: '3' },
props: { tag: {...}, currentPage: 3 }
}
]
これにより、Astro は:
-
/tags/travel/page/1/
-
/tags/travel/page/2/
-
/tags/travel/page/3/
という 3つの静的HTMLページを生成してくれるわけです😄
変数 paths とは?
getStaticPaths() では、変数 paths を定義して、最後に return で値を返しています。
ん?この paths はどこで使うんですかい?という人もいるかも知れないのでが、 paths は Astro がビルドする際に参照するデータです。
Astro は getStaticPaths() から return された paths の情報を使って、
「どの URL に対して、どんなデータを渡してページを生成するか」 を判断しています。
たとえば getStaticPaths() が次のように返した場合
return [
{
params: { slug: 'travel', page: '1' },
props: { tag: travelTagObject, currentPage: 1 }
},
{
params: { slug: 'travel', page: '2' },
props: { tag: travelTagObject, currentPage: 2 }
}
];
Astro は
🔧 1. ビルド時にそれぞれの params を URL にマッピング
-
params ⇒ 生成されるURL
-
{ slug: ‘travel’, page: ‘1’ } ⇒ /tags/travel/page/1/
-
{ slug: ‘travel’, page: ‘2’ } ⇒ /tags/travel/page/2/
🔧 2. それぞれのページに対して、props の内容を Astro.props 経由で注入
const { tag, currentPage } = Astro.props;
スクリプトのこの箇所で、それぞれのページで違うデータを使ってレンダリングされます。
🔧 3. 最終的に /dist に静的HTMLとして出力
/dist/tags/travel/page/1/index.html
/dist/tags/travel/page/2/index.html
のように、すべてのパターン分のファイルが生成されます😄
html実装
ちょっと長くなっちゃいましたね😅
んで、htmlですが、
"ja">
"UTF-8" />
{tag.name} の記事一覧 ページ {currentPage}
{fetchError ? (
"color:red;">記事の取得に失敗しました。
) : posts.length === 0 ? (
このページには記事がありません。
) : (
)}
こんな感じになります!
ビルドして試しに
/tags/travel/page/1/
にアクセスしてみると…
バッチリです!
「次のページ」のリンク先に遷移すると

こちらもバッチリです!
おまけ:ページ番号ナビゲーション
ページ番号を表示させたい時は、まずスクリプトの
const { tag, currentPage } = Astro.props;
の下に
const totalPages = Math.ceil(tag.count / 4);
を追加します。
そしてhtmlに
を追加。
そして一番下にcssを
と追加しておきます。
そしてビルドすると

バッチリです!
/pages/tags/[slug]/page/[page].astro
のここまでのソースは下記になります!
---
export async function getStaticPaths() {
const tagRes = await fetch('http://headless.local/wp-json/wp/v2/tags');
const tags = await tagRes.json();
const paths = [];
for (const tag of tags) {
const count = tag.count;
const totalPages = Math.ceil(count / 4);
for (let page = 1; page // paramsは、URLのパラメータを指定する
params: {
slug: tag.slug,
page: page.toString(),
},
props: {
tag,
currentPage: page,
},
});
}
}
return paths;
}
const { tag, currentPage } = Astro.props;
const totalPages = Math.ceil(tag.count / 4);
const offsetRes = await fetch(`http:
let posts = [];
let fetchError = false;
try {
if (!offsetRes.ok) throw new Error(`記事取得失敗: ${offsetRes.status}`);
posts = await offsetRes.json();
} catch (err) {
console.error("記事取得エラー:", err);
fetchError = true;
}
---
"ja">
"UTF-8" />
{tag.name} の記事一覧 ページ {currentPage}
{fetchError ? (
"color:red;">記事の取得に失敗しました。
) : posts.length === 0 ? (
このページには記事がありません。
) : (
)}
それではでは😊
Views: 2