一覧ページの表示が重い、記事やユーザーが増えてきて「全部を一気に出すのはさすがにマズい」——そんなときに入れるのがページネーション(ページ分割)です。Railsだと Pagy と Kaminari の2択でまず迷うのではありませんか?
「どっちを入れればいいの?」「Pagyが速いって聞くけど、どれくらい違うの?」という疑問に、この記事で先にケリをつけます。結論の使い分け → 比較表 → それぞれの最小実装 → つまずき対処、の順で、実務でgemを選ぶときの判断軸をまるごと渡します。読み終わる頃には、あなたのプロジェクトでどちらを入れるか迷わなくなるはずです。
まず結論:どっちを使えばいい?
細かい比較は後半でしますが、先に判断の軸だけ渡しておきます。
| こんなプロジェクト | おすすめ |
|---|---|
| 新規/速度とメモリ重視/依存を薄くしたい/一覧が多くて重い | Pagy |
| すでに Kaminari で動いている/ビューヘルパーの手厚さ・情報量を取りたい | Kaminari |
| とにかく1つ選べと言われたら | Pagy(軽量・高速・後述の理由) |
ひとことで言えば、「迷ったら Pagy、すでに Kaminari なら無理に乗り換えない」です。新規で入れるなら、速度・メモリ・依存の薄さで Pagy が有利です。一方、既存アプリがすでに Kaminari で回っているなら、動いているものはそのままで十分です。

PagyとKaminariの違いを比較表で整理する
2つの違いは「軽さ・速さ」と「手厚さ・情報量」のトレードオフだと捉えると分かりやすいです。主要な違いをまとめました。
| 観点 | Pagy | Kaminari |
|---|---|---|
| 速度(公式ベンチ) | 約40倍速い | 基準 |
| メモリ(公式ベンチ) | 約1/36 | 基準 |
| 設計 | 件数と現在ページだけを持つ軽量設計 | モデルに page / per スコープを生やす |
| 依存 | ほぼ無し(プレーンなRuby) | Railsエンジンとして統合 |
| ビューヘルパー | 必要十分(HTMLは素なのでCSSを当てる) | 手厚い(テンプレを生成してカスタム可) |
| メンテ状況 | 活発(2026年も更新。最新は v43系) | 安定だが更新は緩やか(安定版 1.2.2) |
| Rails 8 対応 | ◯ | ◯ |
| 情報量・実績 | 増加中 | 長年の実績・日本語情報が豊富 |
速度・メモリの数値は Pagy 公式が公開しているベンチマーク(同じナビゲーションを同じ件数で描画したときの比較)に基づくものです。あくまで公式の計測条件での値なので絶対視は禁物ですが、「Pagyのほうがかなり軽い」という傾向はどのベンチでも共通しています。
なぜPagyは速くて軽いのか
数字だけ見ると「なんでそんなに差が出るの?」と思いますよね。理由はシンプルで、2つのgemが「ページネーションのために何を持つか」がまったく違うからです。

Kaminari はモデルに page / per というスコープを生やし、ページごとの情報をActiveRecord寄りの仕組みで扱います。手軽で分かりやすい反面、そのぶん生成されるオブジェクトが多くなります。
対して Pagy が保持するのは、基本的に「全体の件数」と「今何ページ目か」だけです。ページ番号のリンク(1, 2, 3 … 次へ)は、この2つの数字から計算して組み立てます。余計なオブジェクトを作らないので、メモリも処理時間も小さく済む、というわけです。とくにページ番号のリンクが多い(20ページ分並ぶような)画面ほど、この差は開いていきます。
Pagyの最小実装(Pagy 43)
ではPagyを実際に入れてみましょう。ここでは2026年時点の最新である Pagy 43系の書き方で進めます。まずはGemfileに追加して bundle install するだけ。初期設定ファイル(イニシャライザ)は必須ではありません。
# Gemfile
gem 'pagy'次に、コントローラで Pagy::Method を include し、一覧を取得している箇所を pagy 経由に書き換えます。1ページあたりの件数は limit: で指定します。
class PostsController < ApplicationController
include Pagy::Method
def index
# @posts = Post.all ← これを↓に置き換える
@pagy, @posts = pagy(:offset, Post.all, limit: 20)
end
endポイントは、pagy が「ページ情報を持つ @pagy」と「そのページ分のレコード @posts」の2つを返すところです。あとはビューで、レコードを普通に表示しつつ、ページ番号のナビゲーションを1行足すだけです。
<% @posts.each do |post| %>
<%= render post %>
<% end %>
<%== @pagy.series_nav %>series_nav が「1, 2, 3 … 次へ」のリンクを出力してくれます。出力されるHTMLは素の状態なので、見た目はCSSやTailwindで整える前提です(このあたりはTailwind CSSの使い方の記事も参考にどうぞ)。
古いPagy(v9以前)を使っている場合の注意
ネット上の記事や既存プロジェクトでは、まだ古いPagyの書き方を見かけることが多いです。Pagyは v43 で書き方が大きく変わったので、以下のような記述を見たら「古い版だな」と判断してください。
| やること | 古い書き方(v9以前) | 新しい書き方(v43) |
|---|---|---|
| コントローラのinclude | include Pagy::Backend | include Pagy::Method |
| ヘルパーのinclude | include Pagy::Frontend | 不要 |
| ページ取得 | pagy(Post.all, limit: 20) | pagy(:offset, Post.all, limit: 20) |
| ビューのナビ | pagy_nav(@pagy) | @pagy.series_nav |
すでに古いPagyで動いているアプリを、無理に最新へ上げる必要はありません。ただ、これから新規で入れるなら最新の書き方に合わせておくと、公式ドキュメントとも食い違わずに済みます。
Kaminariの最小実装
次はKaminari。長年Railsで使われてきた定番で、日本語の情報も豊富です。こちらもGemfileに追加して bundle install します。
# Gemfile
gem 'kaminari'Kaminariは、モデルに生えた page / per スコープを呼ぶだけ。include などの準備は不要です。1ページあたりの件数は per で指定します。
class PostsController < ApplicationController
def index
@posts = Post.page(params[:page]).per(20)
end
endビューでは paginate ヘルパーを1行置くだけでナビゲーションが出ます。
<% @posts.each do |post| %>
<%= render post %>
<% end %>
<%= paginate @posts %>見た目を細かく作り込みたいときは rails g kaminari:views default でテンプレートを生成し、直接編集できます。「ページャのHTMLを自分のプロジェクトに置いて、がっつりカスタムしたい」ケースではKaminariの手厚さが効いてきます。
なお、Kaminariの安定版は 1.2.2 で、リリースの更新ペースは緩やかです。Rails 8でも動作しますが、「今も活発に開発が続いているgemがいい」という基準なら Pagy に分があります。
つまずきポイント逆引き
導入時によくハマるところを、原因と対処でまとめておきます。
| 症状 | 原因と対処 |
|---|---|
Pagyで undefined method 'pagy' | コントローラの include Pagy::Method を書き忘れ。共通化したいなら ApplicationController に置く |
| Pagyのナビが「ただのリンク」で崩れる | 出力HTMLは素の状態。CSS/Tailwindで見た目を当てる。Bootstrap等の枠も series_nav(:bootstrap) で選べる |
| Kaminariで2ページ目に飛べない | page(params[:page]) を渡し忘れ。page(1) のように固定していないか確認 |
| 件数の多いテーブルでページ表示自体が重い | 総件数の COUNT が重い。件数を出さない方式(Pagyなら件数レスの paginator)に切り替える手もある |
ページネーション+includesでN+1も一緒に潰す
一覧ページが重い原因は、実は「件数の多さ」より N+1クエリ のことが多いです。ページネーションで「1ページ分だけ」に絞ったうえで、関連データを includes でまとめて引くと、表示は一気に軽くなります。
# Pagyの場合
@pagy, @posts = pagy(:offset, Post.includes(:user, :tags), limit: 20)
# Kaminariの場合
@posts = Post.includes(:user, :tags).page(params[:page]).per(20)「ページで件数を絞る」×「includes で関連をまとめて引く」は、一覧高速化の王道の組み合わせです。rails ブログ機能の実装方法7選のような記事一覧UIでも、この2つをセットで入れておくと安心です。アバター画像を表示する一覧のように、行ごとに関連レコードや画像を出す画面では特に効果が分かりやすいです。
ケース別のおすすめ
「で、自分の場合は?」に答えるために、よくあるケースで整理します。
Pagyが向くケース
- 新規アプリで、これからページネーションを入れる
- 一覧が多い・件数が多く、速度とメモリを重視したい
- 依存gemを増やしたくない・軽い構成を保ちたい
- APIのJSONレスポンスにもページ情報を載せたい(軽量なので相性が良い)
Kaminariが向くケース
- すでにKaminariで動いていて、乗り換える理由が特にない
- ページャのHTMLをテンプレートとして生成し、がっつり作り込みたい
- チームにKaminariの知見があり、日本語情報を頼りたい
迷ったときのひとことルールは、「新規なら Pagy、既存の Kaminari は据え置き」。動いている認証やページネーションはそれ自体が資産なので、速さのためだけに慌てて乗り換える必要はありません。技術の選定に迷ったときの調べ方はエンジニアの仕事は「知っていること」より「調べられること」もどうぞ。
まとめ
Railsのページネーションgem、PagyとKaminariの使い分けを最後に整理します。
- Pagy:件数と現在ページだけを持つ軽量設計で、公式ベンチでは約40倍速い・メモリ約1/36。新規ならまずこちら。最新の v43 は
include Pagy::Method→pagy(:offset, ...)→@pagy.series_nav - Kaminari:
page/perで直感的、ビューの作り込みが得意で日本語情報も豊富。安定版 1.2.2 でRails 8でも動く - 数値はPagyやKaminari公式ベンチが根拠。傾向として「Pagyが軽い」は共通
- 判断軸は「新規なら Pagy、既存の Kaminari は無理に乗り換えない」。ページネーション+
includesで一覧はさらに軽くなる
一覧が重いと感じたら、まずはページネーションと includes の合わせ技から。新規プロジェクトなら、この機会に gem 'pagy' を入れて series_nav を1行足すところから始めてみてはいかがでしょうか。
スポンサーリンク
