Pagination
Navigate through paginated data with first-class Pagy integration and multiple display variants.
Key Features
- Pagy Integration - First-class support for the Pagy gem
- Standalone Mode - Use without Pagy with manual page config
- 4 Variants - Simple, Numbered, Full, Compact
- 5 Sizes - xs, sm, base, lg, xl
- 3 Shapes - Rounded, Square, Pill
- 4 Alignments - Left, Center, Right, Between
- All Colors - Semantic and Tailwind colors for active page
- Icon Only Mode - Hide text labels, show only icons
- Page Jumper - Go-to-page input field
- Turbo Integration - AJAX pagination with Turbo Frames
- Info Display - "Showing X-Y of Z" text
- Per-Page Selector - Optional items-per-page dropdown
- Custom Labels - Override Previous/Next text
- Full Accessibility - ARIA navigation landmark
Basic Usage
The simplest way to use pagination is with a Pagy object from your controller.
# In your controller:
def index
@pagy, @posts = pagy(Post.all, limit: 20)
end
# In your view:
<%= rui_pagination(pagy: @pagy) %>
Standalone Usage
Use pagination without Pagy by providing page configuration manually.
<%= rui_pagination(
current_page: 5,
total_pages: 20,
total_count: 200,
base_url: posts_path,
show_info: true
) %>
Variants
Choose from three display variants based on your UI needs.
Simple
Previous/Next buttons only - minimal and clean.
Numbered (Default)
Previous, page numbers with ellipsis, Next.
Full
First, Previous, page numbers, Next, Last - complete navigation.
<%# Simple - Previous/Next only %>
<%= rui_pagination(pagy: @pagy, variant: :simple) %>
<%# Numbered - With page numbers (default) %>
<%= rui_pagination(pagy: @pagy, variant: :numbered) %>
<%# Full - First/Previous/Numbers/Next/Last %>
<%= rui_pagination(pagy: @pagy, variant: :full) %>
<%# Compact - Minimal "Page X of Y" display %>
<%= rui_pagination(pagy: @pagy, variant: :compact) %>
Icon Only
Show only navigation icons without text labels. Ideal for tight spaces or when paired with page numbers.
Default (with text)
Icon only
Simple variant with icon only
<%# Hide Previous/Next text, show only icons %>
<%= rui_pagination(pagy: @pagy, icon_only: true) %>
<%# Works with any variant %>
<%= rui_pagination(pagy: @pagy, variant: :simple, icon_only: true) %>
<%= rui_pagination(pagy: @pagy, variant: :full, icon_only: true) %>
Compact Variant
A minimal pagination display showing just "Page X of Y" with prev/next buttons. Perfect for mobile or space-constrained layouts.
Compact pagination
Compact with info display
Compact with different sizes
<%# Minimal "Page X of Y" display %>
<%= rui_pagination(pagy: @pagy, variant: :compact) %>
<%# Compact with info %>
<%= rui_pagination(pagy: @pagy, variant: :compact, show_info: true) %>
<%# Smaller compact for tight spaces %>
<%= rui_pagination(pagy: @pagy, variant: :compact, size: :sm) %>
Alignment
Control horizontal alignment of pagination content. Useful when pagination doesn't fill the container width.
align: :left
align: :center
align: :right
align: :between
<%# Left aligned %>
<%= rui_pagination(pagy: @pagy, align: :left, show_info: false) %>
<%# Center aligned %>
<%= rui_pagination(pagy: @pagy, align: :center, show_info: false) %>
<%# Right aligned %>
<%= rui_pagination(pagy: @pagy, align: :right, show_info: false) %>
<%# Space between (default) - info on left, nav on right %>
<%= rui_pagination(pagy: @pagy, align: :between, show_info: true) %>
Page Jumper
Add a "Go to page" input field that lets users jump directly to a specific page number.
How It Works
- Type a page number and press Enter (or blur the field)
- Automatically validates within range (1 to total pages)
- Works with Turbo Frames when
turbo_frame:is set
Pagination with jumper
Compact with jumper
Full pagination with info and jumper
<%# Add go-to-page input %>
<%= rui_pagination(pagy: @pagy, show_jumper: true) %>
<%# Compact variant with jumper - minimal but powerful %>
<%= rui_pagination(pagy: @pagy, variant: :compact, show_jumper: true) %>
<%# Works with Turbo Frames %>
<%= rui_pagination(
pagy: @pagy,
show_jumper: true,
turbo_frame: "posts-list"
) %>
Sizes
Five size variants to match your design.
xs
sm
base
lg
xl
<%= rui_pagination(pagy: @pagy, size: :xs) %>
<%= rui_pagination(pagy: @pagy, size: :sm) %>
<%= rui_pagination(pagy: @pagy, size: :base) %> <%# default %>
<%= rui_pagination(pagy: @pagy, size: :lg) %>
<%= rui_pagination(pagy: @pagy, size: :xl) %>
Shapes
Three shape variants for button corners.
rounded
square
pill
<%= rui_pagination(pagy: @pagy, shape: :rounded) %> <%# default %>
<%= rui_pagination(pagy: @pagy, shape: :square) %>
<%= rui_pagination(pagy: @pagy, shape: :pill) %>
Colors
The active page button uses the specified color. Supports all semantic and Tailwind colors.
<%# Semantic colors %>
<%= rui_pagination(pagy: @pagy, color: :primary) %>
<%= rui_pagination(pagy: @pagy, color: :success) %>
<%= rui_pagination(pagy: @pagy, color: :danger) %>
<%# Tailwind colors %>
<%= rui_pagination(pagy: @pagy, color: :violet) %>
<%= rui_pagination(pagy: @pagy, color: :pink) %>
Info Display
Show "Showing X-Y of Z" information alongside pagination.
With info (default when total_count provided)
Without info
<%# Show "Showing 11-20 of 100" info %>
<%= rui_pagination(pagy: @pagy, show_info: true) %>
<%# Hide info text %>
<%= rui_pagination(pagy: @pagy, show_info: false) %>
Turbo Integration
Enable AJAX pagination with Turbo Frames for seamless page navigation without full reloads.
How It Works
- Wrap your content in a
<turbo-frame>with an ID - Pass that ID to
turbo_frame:parameter - Clicking pagination links updates only that frame
<%# Wrap content in turbo-frame %>
<turbo-frame id="posts-list">
<% @posts.each do |post| %>
<%= render post %>
<% end %>
<%# Pagination inside the frame %>
<%= rui_pagination(
pagy: @pagy,
turbo_frame: "posts-list",
turbo_action: :replace
) %>
</turbo-frame>
<%# Or pagination outside the frame targeting it %>
<%= rui_pagination(
pagy: @pagy,
turbo_frame: "posts-list"
) %>
Custom Labels
Override the default button text for internationalization or custom wording.
<%= rui_pagination(
pagy: @pagy,
variant: :full,
prev_text: "Back",
next_text: "Forward",
first_text: "Start",
last_text: "End"
) %>
<%# For i18n %>
<%= rui_pagination(
pagy: @pagy,
prev_text: t("pagination.previous"),
next_text: t("pagination.next")
) %>
Window Configuration
Control how many page numbers appear around the current page and at the edges.
Default (window: 2, outer_window: 1)
Large window (window: 4, outer_window: 2)
Minimal (window: 1, outer_window: 0)
<%# window: pages shown around current page %>
<%# outer_window: pages shown at edges (first/last) %>
<%= rui_pagination(
pagy: @pagy,
window: 2, # Show 2 pages before/after current
outer_window: 1 # Show 1 page at start/end
) %>
Custom Styling
Override default styles using the class: parameter.
Available Class Overrides
class:- Applied to the wrapper element (merged with defaults)- Any HTML attribute via
**options(id, data-*, aria-*, etc.)
<%# Add background and padding to wrapper %>
<%= rui_pagination(
pagy: @pagy,
class: "p-4 bg-zinc-100 rounded-lg"
) %>
<%# Add custom data attributes %>
<%= rui_pagination(
pagy: @pagy,
id: "my-pagination",
data: { controller: "my-pagination" }
) %>
With Table Component
When using the Table component,
pagination is automatically rendered in the footer when you pass a pagy: object.
See the Table documentation for full table pagination examples including sorting, selection, and toolbar integration.
<%# Table automatically renders pagination when pagy is provided %>
<%= rui_table(pagy: @pagy) do |table| %>
<% table.with_column(key: :name, label: "Name") %>
<% table.with_column(key: :email, label: "Email") %>
<% @users.each do |user| %>
<% table.with_row(id: user.id, data: user) %>
<% end %>
<% end %>
<%# Or use standalone pagination below the table %>
<%= rui_table do |table| %>
...
<% end %>
<%= rui_pagination(pagy: @pagy) %>
Accessibility
The Pagination component follows WAI-ARIA best practices.
ARIA Attributes
-
role="navigation"on the wrapper element -
aria-label="Pagination navigation"for screen readers -
aria-current="page"on the current page button -
aria-disabled="true"on disabled prev/next buttons
Keyboard Navigation
- Tab moves focus between pagination buttons
- Enter / Space activates the focused button
- Focus ring visible on all interactive elements
API Reference
rui_pagination
Pagination component for navigating through paginated data
| Parameter | Type | Default | Description |
|---|---|---|---|
| pagy | Pagy | — | Pagy object for automatic configuration |
| current_page | Integer |
1
|
Current page number (if no pagy) |
| total_pages | Integer |
1
|
Total number of pages (if no pagy) |
| total_count | Integer | — | Total item count (for info display) |
| base_url | String |
current URL
|
Base URL for pagination links |
| page_param | Symbol |
:page
|
URL parameter name for page number |
Appearance
Visual styling options
| Parameter | Type | Default | Description |
|---|---|---|---|
| variant | Symbol |
:numbered
|
Display variant
:simple
:numbered
:full
:compact
|
| size | Symbol |
:base
|
Button size
:xs
:sm
:base
:lg
:xl
|
| shape | Symbol |
:rounded
|
Button shape
:rounded
:square
:pill
|
| color | Symbol |
:primary
|
Active page button color |
| align | Symbol |
:between
|
Content alignment
:left
:center
:right
:between
|
| icon_only | Boolean |
false
|
Show only icons on prev/next buttons (no text) |
Info Display
Information text options
| Parameter | Type | Default | Description |
|---|---|---|---|
| show_info | Boolean |
true
|
Show 'Showing X-Y of Z' info |
| show_per_page | Boolean |
false
|
Show per-page selector dropdown |
| per_page_options | Array |
[10, 25, 50, 100]
|
Options for per-page selector |
| items_per_page | Integer |
from pagy or 10
|
Current items per page |
| show_jumper | Boolean |
false
|
Show go-to-page input field |
Window Configuration
Control page number display
| Parameter | Type | Default | Description |
|---|---|---|---|
| window | Integer |
2
|
Number of page links around current page |
| outer_window | Integer |
1
|
Number of page links at edges (first/last) |
Turbo Integration
AJAX pagination options
| Parameter | Type | Default | Description |
|---|---|---|---|
| turbo_frame | String | — | Turbo Frame ID for AJAX updates |
| turbo_action | Symbol |
:replace
|
Turbo action type
:replace
:advance
|
Custom Labels
Override default button text
| Parameter | Type | Default | Description |
|---|---|---|---|
| prev_text | String |
"Previous"
|
Previous button text |
| next_text | String |
"Next"
|
Next button text |
| first_text | String |
"First"
|
First button text (full variant) |
| last_text | String |
"Last"
|
Last button text (full variant) |
Custom Styling
Override default styles
| Parameter | Type | Default | Description |
|---|---|---|---|
| class | String | — | Custom CSS classes for wrapper (merged with defaults) |
| id | String | — | Custom ID for the wrapper element |
| data | Hash | — | Custom data attributes |