SCALE — Build Lab
UI部品 · REACT COMPONENT

ページネーション部品

CATEGORYUI部品 TYPEReact Component EFFORT60〜120分 DIFFICULTY
PRIMARY CODE
tsx
export function Pagination({
  page, totalPages, onChange,
}: {
  page: number; totalPages: number; onChange: (p: number) => void;
}) {
  if (totalPages <= 1) return null;

  // 表示ページ計算: 1, 2, ..., current-1, current, current+1, ..., last-1, last
  const pages = new Set<number>();
  pages.add(1); pages.add(totalPages);
  for (let i = page - 1; i <= page + 1; i++) {
    if (i >= 1 && i <= totalPages) pages.add(i);
  }
  const sorted = [...pages].sort((a, b) => a - b);

  const items: (number | '...')[] = [];
  for (let i = 0; i < sorted.length; i++) {
    items.push(sorted[i]);
    if (i < sorted.length - 1 && sorted[i + 1] - sorted[i] > 1) items.push('...');
  }

  return (
    <div style={{ display: 'flex', gap: '.35rem', alignItems: 'center' }}>
      <button onClick={() => onChange(Math.max(1, page - 1))} disabled={page === 1}>←</button>
      {items.map((it, i) => it === '...' ? (
        <span key={'el-' + i} style={{ padding: '0 .5rem', color: '#71717a' }}>…</span>
      ) : (
        <button key={it} onClick={() => onChange(it)}
          style={{ fontWeight: it === page ? 700 : 400, color: it === page ? '#a5b4fc' : 'inherit' }}>
          {it}
        </button>
      ))}
      <button onClick={() => onChange(Math.min(totalPages, page + 1))} disabled={page === totalPages}>→</button>
    </div>
  );
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 任意のダッシュボードに組み込み

ページネーション部品

:LiTarget: 用途

前/次 + ページ番号表示。1…5 6 7 …100 形式の省略表示対応。

:LiSparkle: 特徴

  • 前/次ボタン
  • ページ番号
  • 省略表示(…)
  • モバイル対応

:LiCode: コード(コピペ用)

export function Pagination({
  page, totalPages, onChange,
}: {
  page: number; totalPages: number; onChange: (p: number) => void;
}) {
  if (totalPages <= 1) return null;

  // 表示ページ計算: 1, 2, ..., current-1, current, current+1, ..., last-1, last
  const pages = new Set<number>();
  pages.add(1); pages.add(totalPages);
  for (let i = page - 1; i <= page + 1; i++) {
    if (i >= 1 && i <= totalPages) pages.add(i);
  }
  const sorted = [...pages].sort((a, b) => a - b);

  const items: (number | '...')[] = [];
  for (let i = 0; i < sorted.length; i++) {
    items.push(sorted[i]);
    if (i < sorted.length - 1 && sorted[i + 1] - sorted[i] > 1) items.push('...');
  }

  return (
    <div style={{ display: 'flex', gap: '.35rem', alignItems: 'center' }}>
      <button onClick={() => onChange(Math.max(1, page - 1))} disabled={page === 1}>←</button>
      {items.map((it, i) => it === '...' ? (
        <span key={'el-' + i} style={{ padding: '0 .5rem', color: '#71717a' }}>…</span>
      ) : (
        <button key={it} onClick={() => onChange(it)}
          style={{ fontWeight: it === page ? 700 : 400, color: it === page ? '#a5b4fc' : 'inherit' }}>
          {it}
        </button>
      ))}
      <button onClick={() => onChange(Math.min(totalPages, page + 1))} disabled={page === totalPages}>→</button>
    </div>
  );
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加