Pagination
Navigation for moving between pages of content.
Installation
Included in poetry-ui — available as
poetry_pagination
the moment you've installed Poetry,
with no per-component step. To own the source and edit it, copy it into your app:
bin/rails g poetry:add pagination
Adapters
Already paginating with kaminari,
pagy (v43+), or
will_paginate? One generator installs an
adapter so those gems render this component. The adapter is generated into your
app — host-owned code with no extra runtime dependency, yours to edit.
(will_paginate is in maintenance mode and receives no new features; the adapter
supports existing apps, but for a new project pick kaminari or pagy.)
bin/rails g poetry:pagination # detects every loaded paginator
bin/rails g poetry:pagination kaminari # or name one: kaminari | pagy | will_paginate
With kaminari, your existing
paginate @products calls just work — the adapter
replaces kaminari's paginator template wholesale, and Poetry options ride the same call.
With pagy, render the nav with
poetry_pagy_nav(@pagy). With
will_paginate, pass the renderer.
<%= paginate @products, siblings: 2 %>
<%= poetry_pagy_nav(@pagy, edges: :icons) %>
<%= will_paginate @products, renderer: PoetryLinkRenderer,
poetry: { current_variant: :filled } %>
Poetry owns the window: the visible pages come from
siblings: and
edges:, one look across all three gems — the gems'
own window options (kaminari's window /
outer_window, will_paginate's
inner_window, pagy's slot count) deliberately do
not apply. At a single page the adapters render nothing, matching the gems'
own behavior. Labels stay Poetry's — pass
previous_label: /
next_label: to localize.
Default
<%= poetry_pagination(current: 4, total: 10, path: ->(page) { "?page=#{page}" }) %>
First page
<%= poetry_pagination(current: 1, total: 10, path: ->(page) { "?page=#{page}" }) %>
Icons only
<%# Upstream pagination-icons-only: "just the previous and next buttons
without page numbers" (pages: false) beside a rows-per-page select -
the buttons keep their chevron + text; mx-0 w-auto undoes the
centered full-width default so the pager sits in the cluster. %>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
<%= poetry_label(for_id: "pagination-rows-per-page", class: "text-sm font-normal") { "Rows per page" } %>
<%= poetry_select(name: "per_page", value: "25", id: "pagination-rows-per-page", class: "w-20") do |select| %>
<% select.with_item(value: "10") { "10" } %>
<% select.with_item(value: "25") { "25" } %>
<% select.with_item(value: "50") { "50" } %>
<% select.with_item(value: "100") { "100" } %>
<% end %>
</div>
<%= poetry_pagination(current: 3, total: 10, pages: false,
class: "mx-0 w-auto", path: ->(page) { "?page=#{page}" }) %>
</div>
Long range
<%= poetry_pagination(current: 8, total: 20, path: ->(page) { "?page=#{page}" }) %>
Short range
<%= poetry_pagination(current: 2, total: 4, path: ->(page) { "?page=#{page}" }) %>
Simple
<%# Upstream pagination-simple: just the page list - edges: :none
drops Previous/Next. %>
<%= poetry_pagination(current: 2, total: 5, edges: :none, path: ->(page) { "?page=#{page}" }) %>
API
Poetry::Ui::Pagination::Component — options are
constructor keywords (the poetry_* helper forwards them);
slots are composed inside the block. Generated from the gem's source documentation.
| Option | Type | Details | Description |
|---|---|---|---|
| current: | Integer | required | The current page number (1-based). |
| current_variant: | Symbol | one of: outline, filled; defaults to :outline |
How the current page link renders: :outline, or :filled for the primary Button treatment (an unambiguous active state). |
| edges: | Symbol | one of: labeled, icons, none; defaults to :labeled |
The Previous/Next treatment: :labeled (chevron + responsive text), :icons (chevron only - table footers), :none (no edge links). |
| label: | String | defaults to "pagination" |
The nav landmark's accessible name. |
| next_label: | String | defaults to "Next" |
The Next link's visible text (hidden on narrow viewports). |
| pages: | Boolean | defaults to true |
Set false to drop the numbered links - the compact two-button pager (pair with edges: :icons). |
| previous_label: | String | defaults to "Previous" |
The Previous link's visible text (hidden on narrow viewports). |
| siblings: | Integer | defaults to 1 |
How many page links flank the current page before gaps elide to ellipses. |
| total: | Integer | required | The total page count. |
Styling
Every part carries a stable data-slot attribute — target
[data-slot=…] from your own CSS to restyle it. State rides
data attributes on the parts below. This contract is verified against rendered DOM in CI.
| Part | Description |
|---|---|
| [data-slot=pagination] | The <nav> landmark (role=navigation, aria-label) around the page list |
| [data-slot=pagination-content] | The <ul> holding every entry as one horizontal row |
| [data-slot=pagination-item] | One <li> per entry - previous/next, a page link, or a gap |
| [data-slot=pagination-ellipsis] | The elided-pages marker between windows - aria-hidden with an sr-only 'More pages' |