Open-source project
ddnexus/pagy avatar
ddnexus/pagy

Pagy: agnostic pagination in plain Ruby, now at version 43

Agnostic pagination in plain ruby

4,998 stars446 forksRubyMIT

At a glance

What is it?
Pagy is an MIT-licensed Ruby pagination gem that works outside Rails and supports offset, keyset, countless and search-server techniques. Version 43 has its own upgrade guide for users of version 9 or earlier, and the upgrade path is the part worth planning for.
Who is it for?
Adopt Pagy if you want pagination that does not assume ActionView, ActiveRecord or Rails, and you are willing to read the version 43 upgrade guide before touching an existing codebase. Skip it if you need a drop-in replacement for will_paginate or Kaminari view helpers.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • ddnexus/pagy4,998 ★ · +8
Star history
Repository2026-09-232026-11-14
ddnexus/pagy5k5k

8 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What Pagy solves, and who ends up needing it

Pagination looks like a solved problem until the collection stops being an ActiveRecord relation. Pagy's stated scope is agnostic pagination in plain Ruby, and the README lists compatibility with all environments and collection types. That matters for JSON:API endpoints, and for anything that pages results coming back from Elasticsearch, Meilisearch, Searchkick or Typesense rather than from a SQL query.

The gem is for Ruby developers who want the page arithmetic and the navigation markup separated from the framework. If your pagination needs are one ActiveRecord model and one Bootstrap nav bar, Pagy is more machinery than the job requires. If your application pages four different backends through four different code paths, the single pagy method is the argument for adopting it.

One pagy method, many paginators

Version 43 collapsed the API around a single call. The technique is chosen by a symbol passed as the first argument: :offset, :keyset, :countish, :countless, :keynav, :search, :calendar and others.

Under the hood the paginators differ in how they learn the total. Offset pagination runs a count query and then takes a slice. Keyset pagination uses an ordered column and a cursor instead of an offset, which the README calls the fastest technique. Countless skips the count entirely, which means the interface cannot show a total or a last page.

Search pagination is a separate path. You extend a model with Pagy::Search, call pagy_search, and pass the result to a search paginator. The available ones are :elasticsearch_rails, :meilisearch, :rails_active_search, :searchkick and :typesense_rails. The README also documents JSON:API output through the jsonapi: true option, which switches the query string to the nested page[number] and page[size] form, and a JSON-client form that renders @pagy.data_hash rather than urls_hash.

One design claim is worth repeating because it affects load time rather than correctness: it autoloads only the methods that you actually use, with almost zero configuration. That is a decision about process footprint, not about pagination logic.

Installing Pagy and paginating a first collection

The project is distributed as the pagy gem on RubyGems and the README points at the documentation site for the quick start. Installation is the ordinary Ruby path: add the gem, or install it directly.

bash
gem install pagy

Inside a Rails application the include goes in the controller, usually application_controller.rb, and then a single call returns both the pagination object and the records.

rb
# Include pagy in your code (usually application_controller.rb)
include Pagy::Method

# Offset-based pagination
@pagy, @records = pagy(:offset, Product.all)

# Keyset-based pagination (fastest technique)
@pagy, @records = pagy(:keyset, Product.order(my_order).all)

The README gives the first line as the offset example and the second as the keyset example. After the call, @pagy holds the pagination state and @records the slice you iterate in the view. If you are serving JSON:API, the same call takes an option and the response is built from urls_hash.

ruby
# JSON:API nested query string. E.g.: ?page[number]=2&page[size]=100
@pagy, @records = pagy(:offset, Product.all, jsonapi: true)
@pagy, @records = pagy(:keyset, Product.order(my_order).all, jsonapi: true)
render json: { links: @pagy.urls_hash, data: @records }

For a plain JSON client, the README renders @pagy.data_hash instead. For search backends, you extend the model first and then paginate the search object rather than a relation:

rb
# Extend your models (e.g. application_record.rb)
extend Pagy::Search

# Paginate with pagy:
search           = Product.pagy_search(params[:q])
@pagy, @response = pagy(:a_search_paginator, search)

The symbol in that last call is a placeholder in the README text; the concrete names are :elasticsearch_rails, :meilisearch, :rails_active_search, :searchkick and :typesense_rails. The README says it works with almost zero configuration.

The version 43 upgrade is the real cost

The README links a dedicated guide for upgrading to 43 from version 9 or earlier.

The upgrade guide at ddnexus.github.io/pagy/guides/upgrade-guide is the document that governs the work.

The second constraint is the count. Choosing :countless removes the total, and with it the last-page link and any "page X of Y" text. Choosing :keyset removes random access to page numbers, because a cursor cannot jump to page 47. Both are correct trade-offs for large tables and both break interfaces that assume numbered pages. Pagy lets you pick the technique per call, which is the point, but it does not make the trade-off disappear.

Pagy against will_paginate and Kaminari

will_paginate and Kaminari are the two gems most Ruby teams reach for first, and the difference is architectural rather than cosmetic. Both grew up inside Rails and expect ActiveRecord or a similar collection interface, and both ship view helpers that render markup for you. Kaminari in particular is built around a configurable theme system and per-model pagination settings.

Pagy keeps the page arithmetic in plain Ruby and treats rendering as a separate concern. The README describes support for server-side rendering or faster client-side rendering for popular CSS frameworks and APIs, which is a different division of labour: the same @pagy object drives server-side helpers such as series_nav and client-side helpers such as series_nav_js, with :pagy, :bootstrap and :bulma styles. The cost is that migrating from Kaminari or will_paginate is not a find-and-replace. The benefit is that a Sinatra route or a JSON:API endpoint gets the same pagination object as a Rails controller, and search-server results can be paged without pretending they are a relation. If your entire pagination surface is one ActiveRecord model rendered with Bootstrap, Kaminari will ask less of you.

Maintenance, licence and what an upgrade actually involves

The repository is not archived and the last push was on 2026-11-02. Recent releases are 43.7.1 on 2026-10-13, 43.8.0 on 2026-10-17 and 43.8.1 on 2026-10-18, so releases keep arriving within the 43.x line. That pattern suggests 43.x is the line to be on, and that staying on an older major means diverging from where fixes land.

The licence is MIT, declared in LICENSE.txt and shown in the README badge. In practical terms MIT permits commercial and closed-source use with attribution and no warranty; it is not a copyleft licence, so it does not impose source-disclosure obligations on your application. That is a description of the licence text, not legal advice, and anything unusual about your distribution model belongs with a lawyer.

The upgrade cost is the maintenance cost. Version 43 is a redesign, so the budget item is not the gem bump, it is the view and controller edits plus whatever your test suite catches. The README states 100% test coverage for Ruby, HTML and JavaScript end-to-end, and the repository carries API test and E2E test workflows, which is reassuring for the gem and irrelevant to whether your own call sites still work.

Editorial conclusion

Adopt Pagy if you want pagination that does not assume ActionView, ActiveRecord or Rails, and you are willing to read the version 43 upgrade guide before touching an existing codebase. Skip it if you need a drop-in replacement for will_paginate or Kaminari view helpers. Verify first that every paginator you use exists in the version 43 docs, that your Ruby version is still supported, and that your views render the new @pagy instance rather than the old helper calls.

Frequently asked questions

Does Pagy work outside Rails?

Yes. The README describes it as agnostic pagination in plain ruby and lists compatibility with all environments and collection types.

How do I install Pagy?

It ships as the pagy gem on RubyGems, so gem install pagy works, and the README points at the quick start guide on the documentation site for setup inside an application.

Which paginators does Pagy provide?

The README lists OFFSET, COUNTISH, COUNTLESS, KEYSET, KEYNAV, SEARCH and CALENDAR techniques, plus the search-server paginators :elasticsearch_rails, :meilisearch, :rails_active_search, :searchkick and :typesense_rails.

Can Pagy return JSON:API formatted links?

Yes. Passing jsonapi: true to the pagy call switches to the nested page[number] and page[size] query string, and the README renders the response from @pagy.urls_hash.

Is upgrading to Pagy version 43 a simple version bump?

Plan for more than a bump: the README directs readers to a separate guide for upgrading to 43 from version 9 or earlier.

Official sources

  1. ddnexus/pagy on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ddnexus-pagy.svg)](https://hysenlabs.com/projects/ddnexus-pagy)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/ddnexus-pagy.svg)](https://hysenlabs.com/projects/ddnexus-pagy)

Was this useful?