# Kaminari: chainable pagination scopes without monkey patching

> Kaminari adds pagination to Ruby web applications as a chainable scope on the model relation, with an engine that renders page links through overridable partials and a configuration set that caps page size and page count per model. The traps are mundane: it never adds an order, and the per scope silently overrides any limit you set.

**kaminari/kaminari** — ⚡ A Scope & Engine based, clean, powerful, customizable and sophisticated paginator for Ruby webapps

- Repository: https://github.com/kaminari/kaminari
- Website: https://github.com/kaminari/kaminari/wiki
- Stars: 8,667 · Forks: 1,074
- Language: Ruby
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/kaminari-kaminari

## No global pollution of Array, Hash or AR::Base

Kaminari's headline feature is a negative one, and it is the reason a lot of teams try the gem. It does not globally pollute Array, Hash, Object or AR::Base. That sounds like a detail until you remember what a paginator usually does, which is reopen the base class or extend the collection type so that every array in the process suddenly grows a page method. A gem that does that changes behaviour for code it has never seen, including gems you did not know were in the dependency tree. Kaminari's approach is to add a scope to the relation instead, so pagination lives on the query you write rather than on the objects that come back. The repository layout backs the scope based claim: instead of one gem there are several, with kaminari-core holding the shared parts, kaminari-activerecord holding the ActiveRecord integration and kaminari-actionview holding the view helpers, plus lib/, misc/ and test/ at the top level.

## page and per are scopes on the relation

The API is a pair of chainable scopes. The page scope selects a page, and the per scope changes the page size:

```ruby
User.page(7)
```

```ruby
User.order(:name).page(7).per(50)
```

What comes back is a general AR::Relation rather than a special collection class, which is the design decision that makes the rest work. Because it stays a relation, any other condition can be chained before or after the paginator scope, and the query is still one query. The documentation is direct about the per scope not being defined on the models themselves but on the page scope, and gives the reason: you will never use per without also giving a page number. It also notes that per works through limit internally, so it overrides any limit set earlier, which is the sort of thing that produces a mysterious result set if you have not read the note. Page numbers start at one, and page(0) returns the same rows as page(1).

## One Gemfile line, then page params[:page] in the controller

Installation on the default Rails stack is a single line in the Gemfile followed by a bundle:

```ruby
gem 'kaminari'
```

```bash
% bundle
```

There is no generator to run and nothing to define in your models or helpers before it works. The controller side is one line, with the page number read from the params hash:

```ruby
@users = User.order(:name).page params[:page]
```

The view side is the familiar paginate helper, and the project documentation shows how the underlying information queries work, so that the numbers in the page links match the query you actually ran:

```ruby
User.count                     #=> 1000
User.page(1).limit_value       #=> 20
User.page(1).total_pages       #=> 50
User.page(1).current_page      #=> 1
User.page(1).next_page         #=> 2
User.page(2).prev_page         #=> 1
User.page(1).first_page?       #=> true
User.page(50).last_page?       #=> true
User.page(100).out_of_range?   #=> true
User.all.paginated?            #=> false
User.page(1).paginated?        #=> true
```

## Kaminari never adds an order to your query

This is the rule that catches people, and it is stated in the project documentation rather than buried. Kaminari does not add an order to queries, and the advice is to include an order in paginated queries to avoid surprises, for example User.order(:name).page(7). The reason is defensible. An implicit order would mean the gem choosing a column, and for a relation with joins that choice can be ambiguous or expensive, and for a large table it can force a sort that changes the cost of the query. Leaving ordering to you keeps the SQL predictable. The practical consequence is that a paginated endpoint without an order clause returns whatever the database considers natural, and page two may repeat or skip rows between requests, which is exactly the kind of bug that shows up in a customer report rather than in a test.

## padding and max_paginates_per bound the page from both sides

Two scopes handle the awkward ends of a collection. The padding scope pads a set of records that is not a multiple of the page size, so the last page of a result set can be made to look uniform:

```ruby
User.order(:name).page(7).per(50).padding(3)
```

Padding, like per, is not defined directly on the model but on the page scope. The other bound is a ceiling, and it can be set per model with a small DSL in the class:

```ruby
class User < ActiveRecord::Base
  max_paginates_per 100
end
```

If a request asks for a larger page size than that variable, the value from max_paginates_per is used instead. The default is nil, which means no maximum is imposed, so a client that sends a large per value can ask for a large page unless you set the cap. A parallel DSL sets the default page size for one model with paginates_per 50, and a third, max_pages, restricts the total number of pages a model can return, which the documentation describes as useful for limiting large collections.

## The helper renders through overridable partials in an engine

The view side is built as a Rails engine, and the project documentation is clear that the pagination helper is basically a collection of links and non-links, with each one rendered through its own partial template inside the engine. That structure is what makes the helper customizable without patching it. Overriding a partial changes the behaviour or the markup of one element, and you can restyle the whole thing without forking the gem. The helper is also I18n aware, and the output is HTML5 by default, using the nav tag, with support for Rails unobtrusive Ajax. The framework neutrality is broader than Rails alone: the documentation lists ActiveRecord, DataMapper, Mongoid and MongoMapper as supported ORMs, Rails, Sinatra and Grape as supported web frameworks, and ERB, Haml and Slim as supported template engines.

## Global defaults and the kaminari:config generator

The application wide settings are set through Kaminari.configure, and the list of knobs is short enough to read in one go: default_per_page is 25, max_per_page and max_pages are nil, window is 4, outer_window, left and right are 0, page_method_name and param_name are both :page, and params_on_first_page is false. The window value is what determines how many page numbers surround the current one, so raising it widens the link strip. Two of these settings exist for compatibility rather than taste. page_method_name lets you rename the scope, so a model that already has a page method or association does not collide, and params_on_first_page exists for the case where you use the ransack_memory gem and hit problems navigating back to the previous or first page. A generator writes the defaults into a file you can edit:

```bash
% rails g kaminari:config
```

It lands in config/initializers, and since it is a plain initializer, the settings are ordinary Ruby executed at boot rather than a separate config format.

## Recent commits, but the newest tag is from 2021

The maintenance picture needs two dates read together. The last push to the repository was on 2026-10-19, so the code is being changed, but the recent release list ends at v1.2.2 from 2021-12-25, with v1.2.1 from the same day and v1.2.0 from 2020-05-16. In other words, the tagged gem available to a new user is several years behind the head of the branch. That is not automatically a problem, since a paginator is stable work and the supported version matrix in the README is wide, listing Ruby from 2.1 through 4.0 and Rails from 4.1 through 8.1, plus Sinatra 1.4 and 2.0, Haml 3 and later, Mongoid 3 and later, MongoMapper 0.9 and later and DataMapper 1.1.0 and later. It does mean that if you install from source you are running code the released gem does not contain, and the CHANGELOG.md file at the top level is where the difference is recorded. The licence is MIT, carried in the repository as MIT-LICENSE, so using it in a commercial application raises no copyleft questions.

## Writing limit and offset by hand is the alternative

The alternative to a paginator gem is not another gem, it is four lines of code. A relation with limit and offset gives you the rows, and the total count gives you the page count, and from there you can render a page strip yourself. That approach has real virtues: no dependency, no scope to reason about, and the SQL is entirely under your control, which matters if your query is unusual. What it does not give you is the parts Kaminari already solved. You would write the window arithmetic for the visible page numbers, the edge case handling for a last page with fewer rows, the parameter handling for the current page, and the partial overrides for styling, and then you would maintain all of it. The other difference in approach is the layer. Kaminari's engine based helpers work across Rails, Sinatra and Grape and across ActiveRecord, Mongoid, DataMapper and MongoMapper, so an application that mixes an ORM with a non Rails framework can still use one paginator. A hand rolled one is tied to the relation API of whichever ORM you happened to use.

## Conclusion

Kaminari suits a Ruby application that needs conventional numbered pages on an ActiveRecord, Mongoid, DataMapper or MongoMapper relation, or a Sinatra or Grape app that wants the same helper without adopting Rails conventions. It does not suit a project that needs cursor based navigation, and it will not save you from writing an order clause, since Kaminari deliberately adds none. Verify the defaults you care about first, most of all the 25 per page value and the max_paginates_per ceiling, by generating the initializer with rails g kaminari:config. The last push to the repository was on 2026-10-19, while the newest tagged release, v1.2.2, dates from 2021-12-25.

## FAQ

### How do I install Kaminari in a Rails application?

Add gem 'kaminari' to the Gemfile and run bundle. There is no configuration step to perform first, and nothing needs to be defined in your models or helpers before the page scope is available.

### What is the default page size in Kaminari and how do I change it?

The default is 25 records per page, set through the default_per_page configuration option. Per model you can set a different default with paginates_per in the class, and cap it with max_paginates_per, which is used whenever a requested page size is larger than the cap.

### Does Kaminari sort the results for me?

No. The documentation states that Kaminari does not add an order to queries, so you should include one, for example User.order(:name).page(7). Without an order, the rows a page returns can shift between requests.

### Which ORMs, frameworks and template engines does Kaminari support?

The README lists ActiveRecord, DataMapper, Mongoid and MongoMapper as supported ORMs, Rails, Sinatra and Grape as supported web frameworks, and ERB, Haml and Slim as supported template engines. The supported version ranges are listed in the same section.

## Sources

- [kaminari/kaminari on GitHub](https://github.com/kaminari/kaminari)
- [License: MIT](https://github.com/kaminari/kaminari/blob/master/LICENSE)
- [Project website](https://github.com/kaminari/kaminari/wiki)
- [README](https://github.com/kaminari/kaminari/blob/master/README.md)
- [Releases](https://github.com/kaminari/kaminari/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kaminari-kaminari
