Senren UI
Components / Pagination

Pagination

Stable

paged lists and tables

Usage example #

Copy this ERB into a Rails view after installing the component. The snippet below is the same code used by the live preview above.

app/views/.../pagination_example.html.erb
<div class="w-full">
  <%= render Senren::PaginationComponent.new(current_page: 3, total_pages: 6, path: "/components?page=:page") %>
</div>

Install this component #

Copy the official component into your app

Use this when you want the Senren-maintained implementation copied into app/components/senren.

Terminal
bin/rails senren:add pagination

Create a custom component with the same conventions

Use this when you need an app-specific static component that follows Senren's ViewComponent structure.

Terminal
bin/rails generate senren:component pagination --no-client

Dependencies are resolved by senren:add: button, link.

At a glance #

Category Data
Class name Senren::PaginationComponent
Stimulus
Variants default
Depends on button, link
Pairs with table, data_table

Source #

app/components/senren/pagination_component.html.erb
<nav <%= tag.attributes(**root_attrs("flex items-center justify-center gap-1", aria: { label: label })) %>>
  <% prev_page = current_page - 1 %>
  <%= link_to "Previous", page_url(prev_page), class: "rounded-md border border-[hsl(var(--senren-border))] px-3 py-2 text-sm #{current_page == 1 ? "pointer-events-none opacity-50" : "hover:bg-[hsl(var(--senren-accent))]"}", aria: { disabled: current_page == 1 } %>

  <% (1..total_pages).each do |page| %>
    <%= link_to page, page_url(page), class: "inline-flex h-9 min-w-9 items-center justify-center rounded-md px-3 text-sm font-medium #{page == current_page ? "bg-[hsl(var(--senren-primary))] text-[hsl(var(--senren-primary-foreground))]" : "text-[hsl(var(--senren-muted-foreground))] hover:bg-[hsl(var(--senren-accent))] hover:text-[hsl(var(--senren-accent-foreground))]"}", aria: { current: (page == current_page ? "page" : nil) } %>
  <% end %>

  <% next_page = current_page + 1 %>
  <%= link_to "Next", page_url(next_page), class: "rounded-md border border-[hsl(var(--senren-border))] px-3 py-2 text-sm #{current_page == total_pages ? "pointer-events-none opacity-50" : "hover:bg-[hsl(var(--senren-accent))]"}", aria: { disabled: current_page == total_pages } %>
</nav>
app/components/senren/pagination_component.rb
# frozen_string_literal: true

module Senren
  class PaginationComponent < BaseComponent
    VARIANTS = { default: '' }.freeze
    SIZES = { md: '' }.freeze

    def initialize(current_page: 1, total_pages: 1, path: nil, label: 'Pagination', class_name: nil, **html)
      super(variant: :default, size: :md, class_name: class_name, **html)
      @total_pages = [total_pages.to_i, 1].max
      @current_page = current_page.to_i.clamp(1, @total_pages)
      @path = path
      @label = label
    end

    attr_reader :current_page, :total_pages, :path, :label

    def page_url(page)
      return '#' unless path

      path.respond_to?(:call) ? path.call(page) : path.to_s.gsub(':page', page.to_s)
    end
  end
end

AI agent rules #

Use for

  • +paged lists and tables

Avoid

  • -infinite scroll feeds

Accessibility #

  • aria-current=page on current; nav landmark with label.