TOOLS BOX/ガイド/ページネーション
Concept

ページネーション

大量のデータを分割し、ページ単位で表示・取得する設計パターン。

一覧表示UXパフォーマンスoffsetlimit

どういう場面で使うか

  • ·一覧に表示するデータが多く、全件取得がパフォーマンスや UX に影響するとき
  • ·URL クエリでページ番号を管理してブラウザ履歴と同期させるとき
  • ·API から limit / offset で分割取得するとき

注意点 / Pitfalls

  • ·総件数を別途取得しないとページ数の計算ができない
  • ·ソートや絞り込み変更時にページ番号をリセットしないと意図しない範囲を表示する
  • ·URL クエリにページ番号を持たせる場合、ページ変更がブラウザ履歴に積まれることを意識する
  • ·カーソルベースページネーションと混同しないよう注意(大量データの前後スキップには不向き)

補足

カーソルベース(keyset)ページネーションは offset/limit より大量データに強いが実装が複雑。最初は offset/limit で成立させ、必要に応じて切り替える。

概要

ページネーションは、大量のデータをページ単位に分割して表示・取得する設計パターンです。 limit(1ページの件数)と offset(何件目から取得するか)の2つのパラメータで実装するのが一般的です。

基本計算

const PAGE_SIZE = 20;
const currentPage = 3; // 1始まり

const offset = (currentPage - 1) * PAGE_SIZE; // 40
const limit = PAGE_SIZE;                        // 20

const totalPages = Math.ceil(totalCount / PAGE_SIZE);

URL クエリでページを管理する

Next.js App Router では URL クエリにページ番号を持たせると、ブラウザ履歴と同期できます。

// /samples?page=2
export default async function Page({ searchParams }) {
  const page = Number(searchParams.page ?? 1);
  const { items, total } = await fetchItems({ page, limit: PAGE_SIZE });
  return (
    <>
      <ItemList items={items} />
      <Pagination currentPage={page} totalPages={Math.ceil(total / PAGE_SIZE)} />
    </>
  );
}

よくある落とし穴

絞り込み条件を変えたときにページ番号をリセットしないと、2ページ目以降で意図しない範囲が表示されます。

// フィルタ変更時は page を削除してリセット
const params = new URLSearchParams(searchParams.toString());
params.set("framework", newFramework);
params.delete("page"); // ← 忘れがち
router.push(`/samples?${params.toString()}`);

カーソルベースとの比較

offset/limit はシンプルですが、大量データのページスキップには不向きです。 SNS の無限スクロールなど大量データ向けにはカーソルベース(keyset)ページネーションが適しています。

関連サンプル

同じテーマや技術スタックを使った実装例