pagy Documentation

Agnostic pagination in plain ruby

RAW Doc

Technical Documentation: ddnexus/pagy

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)








Pagy the leaping gem!

Agnostic pagination in plain Ruby

[!TIP]
⭐ If you like Pagy, please support it with a Star! ⭐

💚 v3 was already quite snappy...




... and now it's more, with less.

  • Compatible with all environments and collection types
  • It can use OFFSET, COUNTISH, COUNTLESS, KEYSET, KEYNAV, SEARCH, and CALENDAR pagination techniques
  • It supports server-side rendering or faster client-side rendering for popular CSS frameworks and APIs
  • It autoloads ONLY the methods that you actually use, with almost zero configuration
  • It boasts 100% test coverage for Ruby, HTML, and JavaScript end-to-end (E2E)

💚 Examples

Pagination code

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)

# Paginate your collection with one of several paginators
@pagy, @records = pagy(...)

See all the available paginators

JSON:API pagination

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 }

JSON-client pagination

ruby
render json: { pagy: @pagy.data_hash, data: @records }

Search server pagination

Available paginators: :elasticsearch_rails, :meilisearch, :rails_active_search, :searchkick, :typesense_rails

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)

# Or get pagy from paginated results:
@results = Product.search(params[:q])
@pagy    = pagy(:a_search_paginator, @results)

Calendar pagination

Combine time-range and offset pagination.

(Available time units: year, quarter, month, week, and day)

ruby
@calendar, @pagy, @records = pagy(:calendar, collection, year: {}, month: {}, offset: {})
erb
<%== @calendar[:year].series_nav(:bootstrap) %>
<%== @calendar[:month].series_nav(:bootstrap) %>
<%== @pagy.info_tag %>
...
<%== @pagy.series_nav(:bootstrap) %>

Server side rendering

`series_nav`

Default :pagy, :bootstrap and :bulma styles shown.

erb
<%== @pagy.series_nav %> 
<%== @pagy.series_nav(:bootstrap) %>
<%== @pagy.series_nav(:bulma) %>

Client side rendering

rb
# pagy.rb initializer
javascript_dir = Rails.root.join('app/javascript')
Pagy.sync_javascript(javascript_dir, 'pagy.mjs') if Rails.env.development?

`series_nav_js`

Faster and responsive

Dynamically fills the container width.

erb
<%== @pagy.series_nav_js %> 
<%== @pagy.series_nav_js(:bootstrap) %>
<%== @pagy.series_nav_js(:bulma) %>

`input_nav_js`

Fastest! Combines navigation and info in minimum space.

erb
<%== @pagy.input_nav_js %> 
<%== @pagy.input_nav_js(:bootstrap) %>
<%== @pagy.input_nav_js(:bulma) %>

💚 Dev Tools

Integrate pagy with your app's themes interactively (watch demo), and ask specific questions to the Pagy AI right in your app! You need only a single line in your page/layout head to get the Pagy Wand and the Pagy AI:

erb
<%== Pagy.dev_tools %>

See Dev Tools

💚 Support and Docs

[!TIP]
⭐ If you like Pagy, please support it with a Star! ⭐

Top 🏆 Adopters

…and 13,500+ more public repos on GitHub.

Top 💯 Contributors

💚 Credits

Special thanks to:

  • JetBrains for their free open source license.
  • Ben Koshy for his contributions to the documentation, user support and interaction with external frameworks.
  • The Stargazers for their support.

💚 Repository Info

How to contribute

See Contributing

Versioning

Branches

  • The master branch is the latest rubygem-published release. It also contains docs and comment changes that don't affect the published code. It is never force-pushed.
  • The dev branch is the development branch with the new code that will be merged in the next release. It could be force-pushed.
  • Expect any other branch to be internal, experimental, force-pushed, rebased and/or deleted even without merging.

💚 License

MIT


Overview (`test/README.md`)

Ruby Test Environment

Ruby is tested with minitest through thor. It also uses holdify as a snapshot system.

Prerequisite

  • Install the gems: bundle install
  • E2E tests use your local Chrome

No other setup required.

Testing

text
❯ bundle exec thor list
test
----
thor test:all                # Test Rubocop + API + Coverage + E2E
thor test:api:all            # Test current and next API
thor test:api:coverage       # Test API coverage
thor test:api:current        # Test current API
thor test:api:next           # Test next API
thor test:e2e:apps [APP]     # Test all E2E apps (or just APP)

Run all checks

To get green light for PRs, just run them all in ~10secs...

bundle exec thor test:all


Overview (`scripts/hooks/README.md`)

Git Hooks

A hook to ensure consistency and automation. Please setup the hook if you contribute to pagy with PRs.

See also the brief description inside the hook file.

Setup

If your system support symlinks: symlink the hook into the .git/hooks dir:

shell
# from the repo root
ln -s ../../scripts/hooks/pre-commit .git/hooks

Copy

Only for filesystem that don't support symlinks: copy the hooks into the .git/hooks dir:

shell
# from the repo root
cp scripts/hooks/pre-commit .git/hooks

CHANGELOG (`docs/CHANGELOG.md`)


icon: versions

:icon-versions:  CHANGELOG


Release Policy

Pagy follows the Semantic Versioning 2.0.0, and introduces BREAKING CHANGES only for MAJOR versions.

We release any new version (MAJOR, MINOR, PATCH) as soon as it is ready for release, regardless of any time constraint, frequency
or duration.

We rarely deprecate elements (releasing a new MAJOR version is just simpler and more efficient). However, when we do, you can
expect the old/deprecated functionality to be supported ONLY during the current MAJOR version.

Given a version number MAJOR.MINOR.PATCH (e.g. 43.8.1):

The gem 'pagy', '~> 43.0' Gemfile entry (without the PATCH number) ensures that the bundle update command will update pagy to
the most recent version WITHOUT BREAKING CHANGES.

Increment the MAJOR version in your Gemfile ONLY when you are ready to handle the BREAKING CHANGES.

Breaking Changes

Follow the Upgrade to 43 Guide.

If you upgrade from version < 9.0.0 see the following:

[!TIP]
If you need to update through multiple versions, reimplementing the updated pagination
from scratch might be faster.

Deprecations

  • Pagy.options: Use Pagy::OPTIONS directly.
  • Pagy.sync_javascript(...): Use Pagy.sync(:javascript, ...) instead.
  • :max_pages option: follow this method instead.
    IMPORTANT: The Issue #890 still affect the :max_pages option, so stop using it ASAP.
  • :client_max_limit and :max_limit options: use :client_limit instead.

Version 43.8.1

  • Fix Active Record connection checkout in keyset pagination (#931)

Version 43.8.0

  • Add Romanian localization (#930)

Version 43.7.1

  • Fix the database error on an out-of-range page with a huge page number (#929)

Version 43.7.0

  • Refactor the Search::Arguments and Searcher classes to allow more flexibility in chaining queres.
  • Fix the non-standard "previous" link relation in headers_hash and JSON:API urls_hash
    • headers_hash emits the conventional rel="prev" in the link header
    • urls_hash returns the :prev key with jsonapi: true, as the JSON:API specification requires
    • urls_hash keeps the :previous key otherwise
  • Fix series_nav_js ignoring the :slots and :compact helper options
    • The :slots and :compact options passed to the series_nav_js helpers were silently ignored
    • The :steps option overrides the :slots option, while the :compact option applies to all the steps
  • Fix the playground doc URLs and make the playground apps consistent
  • Add :rails_active_search paginator, test and docs

Version 43.6.3

  • Deprecate the :max_limit in favor of the more descriptive :client_limit option.

Version 43.6.2

  • Fix nil records returned for Array collections on page overflow (#920) (Fix #919)

Version 43.6.1

  • HTML escape URL strings in href attributes (Fix #914)

Version 43.6.0

  • Add the max_result_window option to the searchkick and elasticsearch_rails paginators to get an accurate count (#910)

Version 43.5.6

  • Validate I18n locale input, coerce dev_tools wand_scale, add input-safety docs (#908)
    • Coerce dev_tools wand_scale to a float
    • Validate the I18n locale against a BCP 47 pattern
    • Update docs and comments about safety

Version 43.5.5

  • Rescue malformed page request param in Pagy::Keyset and KeynavJsPaginator (#907)
    • Rescue malformed page request param in Pagy::Keyset and KeynavJsPaginator
    • Simplify decoding logic

Version 43.5.4

  • Fix SERIES_SLOTS loading error (close #902)

Version 43.5.3

  • Autoload series, a_lambda and page_label
  • Add Hungarian localization file for Pagy (#896)

Version 43.5.2

  • Add type validation for page and limit keys type (close #895)
  • Simplify series_nav_js removing "pagy-rjs" CSS class (Fix #894)

Version 43.5.1

  • Remove ghost code from cli; improve tests

Version 43.5.0

  • Update javascripts according to es-linting
  • Update min ruby version to 3.3 (remove EOL 3.2)
  • Implement the NEW pagy/next entrypoint to run the NEXT version now
  • Refactor deprecations:
    • Deprecated :client_max_limit in favor of :max_limit; moved code into deprecation.rb
    • Move deprecated options out of class code, to keep the code clean
  • Remove RBS resources. Not worth the maintenance effort.

Version 43.4.4

  • Make typecasting in Keyset based classes safer and more efficient

Version 43.4.3

  • Deprecate the :max_pages option (close #890)

Version 43.4.2

  • Fix edge-case for queries with multiple orderings on same column (#888)

Version 43.4.1

  • Normalize deprecations

Version 43.4.0

  • Improve stylesheets and docs
  • Improve JavaScript:
    • Add sync method and task
    • Deprecate sync_javascript
    • Simplify build, configuration and docs

Version 43.3.3

  • Fix Request#resolve_page with jsonapi, limit, and missing page param (#885)
  • Fix pagy-tailwind.css inconsistencies
  • Improve ts/js build process and wand help
  • Move the next_tag into the Pagy class

Version 43.3.2

  • Implement NumericUI module to avoid including the numeric helpers in keynav classes
  • Improve offset accessors and update docs
  • Add basic RBS

Version 43.3.1

  • Update assets for a few apps
  • Fix pagy.ts /.js input_nav update

Version 43.3.0

  • Add :typesense_rails paginator (See typesense/typesense-rails#17)
  • Accept a variable number of search arguments
  • Improve resilience of internal pagy nav method
  • Use Pagy::OPTIONS instead of Pagy.options for efficiency
  • Simplify assign options

Version 43.2.10

  • Add failing test and revert code that caused empty aria label
  • Avoid invalid limit param (similar to #862)
  • Add thread flag to /bin/pagy
  • Add uri gem dependency to the gemspec
  • Freeze the @order in key* apps
  • Replace require with require_relative in Pagy::CLI
  • Make Pagy::OPTIONS thread-safe

Version 43.2.9

  • Fix NoMethodError with tampered params (#872)

Version 43.2.8

  • Add fallback to 'en' for unknown locale. Implement #868.
  • Fix resolve_page with empty page in classes with non-integer page. Close #863
  • Simplify keyset syntax

Version 43.2.7

  • Add a hint predicate for DB optimizers, to multi-column keysets
  • Improve code readability and style
  • Refactor anchor tag helpers

Version 43.2.6

  • Refactor i18n:
    • Raise exceptions for missing 'pagy' and 'p11n' keys in the dictionary
      file
    • Improve efficiency and readability

Version 43.2.5

  • Fix Pagy::I18n.locale to ensure to_s. Close #861.
  • Reduce endless methods to very short, paramless ones
  • Refactor the in_range? method:
    • Remove implicit assignation to empty page variable
  • Enforce freezing the Pagy::Request object and params
  • Refactor calendar:
    • Remove the marshaling
    • Improve use and naming of locals
    • Improve readability
  • Refactor linkable:
    • Improve use of locals
    • Extend conditional sub to fragment
    • Improve readability
  • Improve simplicity and readability

Version 43.2.4

  • Fix anchor_string option not being read from @options in a_lambda. Close #857
  • Fix page '0' raising an exception

Version 43.2.3

  • Remove rerun
  • Improved direction handling in CSSs

Version 43.2.2

  • Refactor bin/pagy to use the Pagy::CLI class
  • Replace optimist with the optparse standard lib
  • Simplify apps by using SQLite :memory:

Version 43.2.1

  • Fix the shallow cloning of root_key params. Close #851.
  • Implements support for easy overriding
  • Add :current_url alias to :page_url
  • Fix limit_tag_js not respecting the passed :client_max_limit option
  • Fix data_hash returning keys with nil values, or URLs without page; add :current_url alias to :page_url
  • Remove warning when fit_time
  • Fix respond_to_missing definition arguments in Search::Arguments

Version 43.2.0

  • Implement :countish paginator
  • Simplify Pagy::Linkable

Version 43.1.8

  • Refactoring of elasticsearch_rails paginator:
    • Fix "response" shadowing. Close #842.
    • Simplify code
    • Improve readability: rename variables and methods in more natural
      and unambiguous language

Version 43.1.7

  • Fix and improve search paginators:
    • Add support for elasticsearch_rails v8
    • Prioritize elasticsearch_rails response over raw_response in
      total_count (causing multiple query executions)
    • Fix the ignored :search_method paginator option
    • Close #837, close #838, close #839.

Version 43.1.6

  • Ensure the request GET and POST merge into a proper params Hash
  • Simplify code efficiency and readability

Version 43.1.5

  • Fix and improve the composition of the page url. Close #835

Version 43.1.4

  • Add full support for legacy countless params, url composition and (limited) behavior

Version 43.1.3

  • Improve the page options handling for countless:
    • Replace the crippled handling of legacy last-less links params
      introduced in 43.1.2 by gracefully starting the pagination from
      the beginning
    • Remove the compose_page_param method (it would need a deep refactoring
      for compatibility with the legacy handling)
    • Improve testing
  • Improve the info message for empty collection in countless paginations

Version 43.1.2

  • Support easy countless page param overriding (for legacy param and behavior). See #816
  • Handle legacy page param for :countless paginator. Close #832
  • Improve mock collections
  • Improve code comments

Version 43.1.1

  • Makes keynav pagination compatible with nested params:
    • Improve the URL composing and unescaping
    • Adds the keynav+root_key.ru showcase app
  • Simplify the request code and arguments

Version 43.1.0

  • Translate the aria_label.nav entry in the id, ja, kn, sw locales. Close #588, close #590, close #591, close #603.
  • Improve the Pagy AI panel
  • Simplify the paginators code
  • Allow nested :page and :limit request params and URLs:
    • Add the :root_key option:
    • Simplify the :jsonapi handling as a special case of nested params
  • Fix and simplify the pagy console code

Version 43.0.7

  • Add compatibility for searchkick 6. Close #831
  • Fix the search paginators request in passive mode. Close #830.

Version 43.0.6

  • Improve Pagy AI Widget scripts

Version 43.0.5

  • Add console loader for easier usage
  • Add comment for the calendar usage. Close #827.

Version 43.0.4

  • Remove the Pagy::OPTIONS from the Calendar configuration. Close #825

Version 43.0.3

  • Update tr.yml (#824)

Version 43.0.2

  • Ensure the Pagy::Request#params are the original params sent with the request. Close #821

Version 43.0.1

  • Reimplement reading params from POST requests; rename internal variables. Close #821
  • Fix AI widget problem for apps. Close #817.
  • Improve I18n documentation. Close #811
  • Link to documentation website's CHANGELOG page (#804)

Version 43.0.0

We needed a leap version to unequivocally signal that it's not just a major version: it's a complete redesign of the legacy
code at all levels, usage and API included.

Why 43? Because it's exactly one step beyond "The answer to the ultimate question of life, the Universe, and everything." 😉

Changes

  • The Countless pagination remembers the last page
    • Pagination navs now allow jumping forward after navigating back a few pages.
  • Javascript refactoring
    • The new Pagy.sync_javascript function used in the pagy.js initializer, avoids complicated configurations.
    • Added the plain pagy.js and relative source map files.
  • I18n refactoring
    • No setup required: the locales and their pluralization are autoloaded when your app uses them.
    • The locale files are easier to override with Pagy::I18n.pathnames << my_dictionaries.
  • HTML and CSS refactoring
    • Stylesheets are now based on CSS properties and calculations, for easier customization.
    • The new PagyWand interactive tool generates the CSS Override for your custom styles and provides live feedback right in your
      app.
  • Playground apps
    • Better usability and styles
  • Bootstrap and Bulma
    • Fixed a few style glitches.

LEGACY CHANGELOG >>>


CHANGELOG LEGACY (`docs/CHANGELOG_LEGACY.md`)


icon: versions layout: page visibility: hidden

LEGACY CHANGELOG

[!WARNING]
Many links on this page may refer to old resources and may return HTTP 404 "Not Found"

Version 9.3.4

  • Fix method visibility in JsonApiExtra (#765)

Version 9.3.3

  • Add test for locales - to find problematic keys (#752)
  • Update locales: zh-CN, zh-HK, zh-TW (#751) (fix #608, fix #609, fix #610)
    • Remove :other from :aria_label key and code comment
    • Change :item_name which had :one_other keys to default to the :other key
    • Fix comment

Version 9.3.2

  • Improve gapped series computation by a few milliseconds

Version 9.3.1

  • Added qualified column names to the keyset query

Version 9.3.0

  • Remove the :typecast_latest variable
  • Add the :jsonify_keyset_attributes variable to override the encoding (#749)

Version 9.2.2

  • Replace inline templates with template block in sinatra apps
  • Replace the rails calendar app with a sinatra app
  • Add PagyApps::INDEX

Version 9.2.1

  • Improve bin/pagy to dynamically find apps and descriptions
  • Apps refactoring:
    • Update rails to 8.0
    • Replace rails AR keyset apps with sinatra apps
    • Improve consistency

Version 9.2.0

  • Simplify the keyset API:
    • Deprecate the :after_latest variable in favour of :filter_newest
    • Add the keyset argument to the :filter_newest lambda
    • Rename protected after_latest_query > filter_newest_query

Version 9.1.1

  • Simplify calendar code

Version 9.1.0

  • Add Dzongkha (dz locale) (#741)

Version 9.0.9

  • Set freeze flag to false (#737)
  • Fix: Prefix DEFAULT with Pagy:: (#736)

Version 9.0.8

  • Keep offset and limit variables untouched by the overflow extra in :empty_page mode (closes #733)

Version 9.0.7

  • Absolutize generic define? to fix module resolution

Version 9.0.6

  • Fix docs (closes #732)
  • Replace pluck with pick

Version 9.0.5

  • Fix for missing variables passed to pagy_anchor from pagy_nav (closes #728)

Version 9.0.4

  • Fix groupdate timezone error on certain machines/config
  • Fix gem containing tmp files

Version 9.0.3

  • Remove legacy and unused javascript files
  • Improve pagy_get_page with force_integer option
  • Fix jsonapi with keyset
  • Complete the internal refactoring of version 9:
    • Remove the pagy*_get_vars methods now useless
    • Use to_i on the page variable for the search extras

Version 9.0.2

  • Rename and document the link header to pagy_link_header
  • Add first and next url helpers to the keyset extra; add the keyset section to config/pagy.rb
  • Fix nil page in keyset URL not overriding the params page
  • Extracted shared method

Version 9.0.1

  • Fix countless executing the count query
  • Rename row_comparison > tuple_comparison; ignore mixed orders

Version 9.0.0

Breaking Changes

Dropped support for deprecated stuff

  • The foundation, materialize, semantic and uikit CSS extras have been removed:
    (See the reasons)
  • Javascript renamed files
    • pagy.js: use pagy.min.js
    • pagy-module.js: use pagy.mjs
    • pagy-dev.js: use the pagy.min.jswith the pagy.min.js.map
    • pagy-module.d.ts: use pagy.d.ts
  • The Array type for the :size (e.g. size: [1, 4, 4, 1]) that generates the classic bar is not supported anymore: use the
    :size set to an integer with the ends: true variable (which are the default since 8.4.6). If a legacy bar remains REALLY a
    requirement, add require 'pagy/extras/size to your pagy.rb initalizer. (See the
    size extra)

Simple renaming

We used items for too many things that confused even maintainers. The "items" are now referencing only the records/elements
fetched from a collection (plural entity), so they are kept as before (e.g. pagy_*get_items methods). The "number of items" (
singular entity), are now unmistakably referenced as the limit everywhere (code, API, files, extras, docs, etc.). The logic
didn't change so you have just to globally search 'items' and replace with limit and you should be done... or use the detailed
table below:

Type Search Replace
Pagy Variable :items :limit
Extra Path pagy/extras/items pagy/extras/limit
Extra Variable :items_param :limit_param
Default URL param items limit
Extra Variable :items_extra :limit_extra
Extra Variable :max_items :limit_max (now inverted)
Extra Method pagy_items_selector_js pagy_limit_selector_js
Extra Variable :headers[:items] :headers[limit]

Code changes

  • The DEFAULT[:anchor_string] was useless and has been dropped.
  • The :anchor_stringand the :fragment are not instance variables anymore, but keyword arguments for all the helpers, because
    it is frontend code (see the discussion). Instead of passing them to the
    pagy* method in the controller, pass it to any pagy_*nav method in the view.
  • A general internal revamp has changed the positional argument for the Pagy::* objects and constructors methods to keyword
    arguments. If you get a wrong number of arguments (given 1, expected 0) (ArgumentError), just use a double splat **.

Possibly breaking overrides

  • The internal Pagy protected methods have been renamed and refactored. If you use custom Pagy classes, you may need to search
    into the code.

Changes

  • Improve Keyset::Sequel and docs
  • BREAKING: Rename :max_limit > :limit_max
  • BREAKING: Rename variable, param, accessor, extra and helper "items" to "limit"
  • Add playground keyset_ar.ru and keyset_s.ru apps and integration with the rest of the gems
  • Add keyset pagination base files
    • Pagy::Keyset API
    • ActiveRecord and Sequel adapters
  • BREAKING: Transform the vars positional hash argument in keyword arguments (double splat); internal renaming of setup/assign
    methods
  • Refactor pagy_get_vars in various backend extras
  • BREAKING: Refactor the fragment url
  • BREAKING: Refactor the anchor_string system
  • BREAKING: Drop the support for 8+ deprecations

Version 8.6.3

  • Add missing DEFAULT[:max_pages] hint to the config/pagy.rb
  • Improve activerecord handling in playground apps
  • Fix the missing "ar.pagy.aria_label.nav.other" (closes #577)

Version 8.6.2

  • Fix the legacy size in code and test
  • Improve code readability and size check in series
  • Fix the old terminology in the demo.ru app

Version 8.6.1

  • Update playground apps and e2e tests
  • Update pagy.rb initializer

Version 8.6.0

  • Add translated pluralized aria_label.nav for "ar" locale (close #577)
  • Deprecate the legacy bar. Insert first and last pages and gaps when needed into the simple bar

Version 8.5.0

  • Improve pagy playground launcher
  • Refactor calendar class structure
  • Remove automatic skipping of bundle install in playground apps
  • Update ruby calendar test
  • Update cypress calendar test
  • Refactor calendar test environment to use activerecord
  • Add code for calendar counts
  • Remove redundant Warning
  • Convert calendar.ru to calendar_rails.ru

Version 8.4.5

  • Fix pluralization rule link on locale files (#716)
  • Install gems in pagy CI
  • Indentation changes
  • Remove :cycle false default
  • Fill aria_label.nav ca pluralized entry (#715) (Fixes #581)
  • Fix typos (#710)

Version 8.4.4

  • Update eslint: new configuration, stricter rules and javascript code

Version 8.4.3

  • Deprecate/rename javascript files keeping copies of old files to avoid production breaking changes; updates playground apps

Version 8.4.2

  • Limit the playground --rerun option to linux platforms
  • Simplify and improve the js environment by using bun

Version 8.4.1

  • Fix pagy.in in pagy_get_items method introduced in 8.4.0 (see #696) (closes #704) (closes #708) (#707)
  • Fix renamed Frontend Helpers to JS Tools and typo next_a "aria-label" (#700)
  • Fix rubocop

Version 8.4.0

  • Retrieve only @in items:
    • improve the performance of the last page in
      particular storage conditions (see #696)
  • Improve pagy launcher for pagy devs

Version 8.3.0

  • Discontinue foundation materialize, semantic and uikit CSS extras
  • Improve playground:
    • Add install option (automated in pagy development)
    • Fix HTML validation for all apps
    • Remove unused styles from the demo app
  • Hardcode version in pagy.gemspec

Version 8.2.2

  • Add nav translation for ko (closes #592) (#690)

Version 8.2.1

  • Fix empty page param raising error (closes #689)

Version 8.2.0

  • Fix the '#pagy_url_for' method for calendar pagination (#688)
  • Extend the use of pagy_get_page to the arel, array and countless extras
  • Add the pagy_get_count method to the backend

Version 8.1.2

  • Added "da" locale for aria_label.nav (closes #583)

Version 8.1.1

  • Fixed broken aria-label for disabled links in Foundation (#685)
  • Simplification of input variables and defaults: params and request_path are not instance variables

Version 8.1.0

  • Implement max_pages to limit the pagination regardless the actual count
  • Improve efficiency of params in pagy_url_for
  • Remove nil variables from DEFAULT
  • Removed redundant @pages, aliased with @last

Version 8.0.2

  • Minor change in rails app and RM run config
  • Fix canonical gem root:
    • Correct script.build: "NODE_PATH="$(bundle show 'pagy')/javascripts"
    • Move pagy.gemspec inside the gem root dir
  • Fix for Turbo not intercepting changes in window.location
  • Use require_relative for gem/lib files
  • Complete translation of aria.nav for "ru" locale (close #599)
  • Docs improvement and fixes

Version 8.0.1

  • Reorganize the gem root dir: it was the lib dir (containing everything), now is the gem dir (containing lib and everything
    else).
  • Fix broken link in README

Version 8.0.0

Breaking changes

  • Renamed/removed the following arguments for all the helpers:
    • Search pagy_id:, replace with id:
    • Search nav_aria_label:, replace witharia_label:
    • The nav_i18n_key has been removed: pass the interpolated/pluralized value as the aria_label: argument
    • The item_i18n_key has been removed: pass the interpolated/pluralized value as the item_name: argument
    • The link_extra: has been removed: its cumulative mechanism was confusing and error prone. The :anchor_string pagy
      variable substitutes it, however it's not an helper argument anymore, so you can assign it as the DEFAULT[:anchor_string]
      and/or pass it as any other pagy variable at object construction. (
      See customize the link attributes)
  • HTML structure, classes and internal methods have been changed: they may break your views if you used custom stylesheets,
    templates or helper overrides. See the complete changes below if you notice any cosmetic changes or get some exception.
  • The navs and support extras has been merged into the new pagy extra.
    Search for "extras/navs" and
    "extras/support" and replace with "extras/pagy" (remove the duplicate if you used both)
  • The "extras/frontend_helpers" has been renamed to "extras/js_tools"
  • The build path for javascript builders has been updated to the canonical paths for gems, and has moved from the lib to
    the gem root. Notice that the correct setup in package json was still wrongly wrapped in the gem dir for 8.0.0-8.0.1, and it
    has finally been fixed in 8.0.2 (sorry for that):
    • 8.0.0-8.0.1 only: build: "NODE_PATH=\"$(bundle show 'pagy')/gem/javascripts\" <your original command>"
    • 8.0.2+: build: "NODE_PATH=\"$(bundle show 'pagy')/javascripts\" <your original command>"

Changes

  • Streamlined HTML and CSS helper structure. You may want to look at the actual output by running
    the pagy demo
    • The pagy_nav and pagy_nav_js helpers output a series of a tags inside a wrapper nav tag (nothing else)
    • The disabled links are so because they are missing the href attributes. (They also have the role="link"
      and aria-disabled="true" attributes)
    • The current and gap classes are assigned to the specific a tags
    • HTML changes
      • All the pagy helper root classes have been changed according to the following rule. For example:
        • "pagy-nav" > "pagy nav"
        • "pagy-bootstrap-nav-js" > "pagy-bootstrap series-nav-js"
        • and so on for all the helpers
      • The active class of the *nav/*nav_js links as been renamed as current
      • The disabled, prev, next and pagy-combo-input link classes have been removed (see
        the stylesheets for details)
      • The rel="prev" and rel="next" attributes have been dropped (they are obsolete)
      • The <label>/</label> and **/** wrappers in the dictionary files have been removed
  • The pagy_link_proc method (only used internally or in your custom overriding) has been renamed to pagy_anchor and it works
    slightly differently:
    • The link_extra: key argument has been removed
      • The extra positional argument of the returned lambda has been removed
      • The classes: and aria_label: keyword arguments have been added to the returned lambda
  • The nav_aria_label_attr method has been renamed as nav_aria_label
  • The internal prev_aria_label_attr and next_aria_label_attr methods have been removed
  • The gap in the nav bars is a disabled anchor element (a tag without a href attribute)
  • The pagy_prev_html and pagy_next_html have been renamed as pagy_prev_a and pagy_next_a
  • The pagy_prev_link_tag and pagy_next_link_tag have been renamed as pagy_prev_link and pagy_next_link
  • The *combo_nav_js and pagy_items_selector_js helpers use a more efficient code
  • The src/pagy.ts and relative built javascript files have been adapted to the above changes
  • The stylesheets are a lot simpler as a consequence of the changes above
  • All the *combo-nav_js of the framework extras use simpler structure and improve the look and feel consistently with their
    respective frameworks
  • All the frontend extra have been normalized and are totally consistent with each other; a few may add the classes:
    argument to a few components, when the framework allows it.
  • Created the pagy playground system of apps working with the pagy executable.
  • Internal renaming FrontendHelpers > JSTools
  • Fix broken link of pagy.rb in docs (closes #668, #669)
  • Docs Improvements
  • Better code issue template

Version 7.0.11

  • Fix jsonapi prev and next keys for unavailable links (#665)
  • Docs fixes

Version 7.0.10

  • Added name attribute to combo and items input tags; removed pagy_marked_link and refactored js input action

Version 7.0.9

  • Improve all pagy apps
  • Normalized bootstrap, bulma, foundation, navs extra

Version 7.0.8

  • Update gems and fix a rubocop bug
  • Add all styles to pagy_styles.ru
  • Better pagy stylesheets
  • Fix for uikit extra prev and next link duplicating chevrons
  • Change aria_Label to aria_label for Arabic locale (#657)

Version 7.0.7

  • Fix for retype excluding linked files and showing category images
  • Fix for first *nav_js page not active with trim (introduced with the #656 fix 7e2f118)
  • Normalize pagy apps; implement pagy_styles.ru

Version 7.0.6

  • Internal renaming of frontend constants
  • Fix for disabled links and missing or extra ARIA attributes in frontend extras
  • Bootstrap fix for current page link; pagy.js fix for trim of current page (closes #656)

Version 7.0.5

  • Updated gems, npm modules, contributors
  • Added the pagy stylesheets to the gem, updated apps, docs and manifest
  • Added note for the metrics and compacted the chart section (closes #652)
  • Docs: fix formatting, grammar in README.md (#654)
  • Add note about PR base branches to readme (#653)

Version 7.0.4

  • Tailwind styles integrated with the pagy-items-selector-js (#646)
  • Deprecated the "pagination" CSS class, use the "pagy" CSS class that has been added to all the interactive pagy helper outputs
  • Fix indentation of cs locale (#648); add "pagy.aria_label.nav.few" entry, duplicating the "other" pluralization
  • Update cs translations (#648)
  • Expand/Correct changes about pagy.prev and pagy.next (#649)

Version 7.0.3

  • Remove extra space in pagy_nav, pagy_nav_js and .pagy-combo-input
  • Refactor of tailwind styles and docs (closes #646)
  • Add pagy_tailwind_app.ru (#646)
  • Add missing CSS breaking change to the CHANGELOG (#646)

Version 7.0.2

  • Fix for missing to fetch count_args default (close #645)
  • Non-code improvements

Version 7.0.1

  • Updates ckb translations to be complaint with ARIA in v7.x.x (#643)

Version 7.0.0

Breaking changes

  • Dropped old rubies support: Pagy follows the ruby end-of-life supported rubies now.
  • Renamed :i18n_key > :item_i18n_key
  • Refactored support extra
    • Renamed pagy_prev_link to pagy_prev_html to avoid confusion with pagy_prev_link_tag
    • Removed pagy_next_link to pagy_next_html to avoid confusion with pagy_next_link_tag
  • Rack 3 breaking changes:
    • The headers extra produces all lowercase headers, regardless how you set
      them see rack issue
    • Removed :escaped_html option from pagy_url_for (only breaking if you override the method or use the option directly)
  • Dictionary structure changes: (affects only app with custom helper/templates/dictionary entries)
    • The nav entry has been flattened: pagy.nav.* entries are now pagy.*:
      • If you have custom helpers/templates: search the keys that contain '.nav.' and replace them with '.'
      • If you have custom dictionary entries (overrides): remove the 'nav:' line and unindent its block
    • A few labels used as aria-label have been added: you may want to add/use them to your custom helper/templates/dictionaries
      for ARIA compliance.
      • pagy.aria_label.nav Pluralized entry: used in the nav element
      • pagy.aria_label.prev, pagy.aria_label.next Single entry: used in the prev/next a link elements

Default changes (possibly breaking test/views)

  • Changed Pagy::DEFAULT[:size] variable defaults from [1, 4, 4, 1] to 7. You can explicitly set/restore it in the
    initializer, if your app was relying on it.
  • Added sensible :size defaults in Calendar Unit subclasses. You can explicitly set it in the initializer, if your app was
    relying on it.
    • Pagy::Calendar::Day::DEFAULT[:size] 31
    • Pagy::Calendar::Month::DEFAULT[:size] 12
    • Pagy::Calendar::Quarter::DEFAULT[:size] 4
    • Pagy::Calendar::Year::DEFAULT[:size] 10
  • Changed a few format defaults in Calendar Unit subclasses. You can explicitly set it in the initializer, if your app was
    relying on it.
    • Pagy::Calendar::Day::DEFAULT[:format] from '%Y-%m-%d' to '%d'
    • Pagy::Calendar::Month::DEFAULT[:format] from '%Y-%m' to '%b'
    • Pagy::Calendar::Quartr::DEFAULT[:format] from '%Y-Q%q' to 'Q%q'

Visual changes (possibly breaking test/views)

  • The ARIA label compliance required the refactoring of all the nav helpers that might look slightly different now:
  • The text for "Prev" and "Next" is now used for the aria-label (actually as "Previous" and "Next") and has been
    replaced in the UI as < and >. You can edit the dictionary entries pagy.prev and pagy.next if you want to revert it to
    the previous default (&lsaquo;&nbsp;Prev and Next&nbsp;&rsaquo;)

CSS changes (possibly looking different/broken)

  • The HTML of the current page in pagy_nav and pagy_nav_js has been changed from simple text (e.g. 5) to a
    disabled link (e.g. <a role="link" aria-disabled="true" aria-current="page">5</a>). That affects your CSS rules and
    the old tailwind examples targeting the page links, now overreaching the current page.
    • You may fix eventual problems either by replacing the affected a rules with narrower a[href] selectors however if you use
      Tailwind we recommend to use the improved tailwind style, that you can
      adapt in no time to anything you need.
  • The extra spaces added between pages of pagy_nav and pagy_nav_js have been removed
  • The pagy-combo-nav-js .pagy-combo-input internal element x-margins have been removed

Internal renaming of private methods (unlikely to break anything)

You should not have used any of the private methods, but if you did so, you will get a NoMethodError
(undefined method...) very easy to fix by simply renaming, because there are no changes in the logic.

Changes

  • Added :count_args variable passed to the collection.count(...) statement (avoids overriding of pagy-gets-vars and
    expands count capabilities)
  • ARIA compliance
  • Removed the pagy templates: they were a burden for maintenance with very limited usage,
    still you can use them
  • Added a simpler and faster nav without gaps (just pass an integer to the :size)
  • Internal renaming of private frontend methods
  • Updated code and tests for latest gem and npm module versions
  • Internal improvements of automation scripts

Version 6.5.0

  • Add ckb: "pagination" entry (#641)

Version 6.4.4

  • Adapt pagy-ci workflow to run on current gemfile (no-failure for new gems enabled in leading version)
  • Fix for wrong arguments types in meilisearch pagy_search extension

Version 6.4.3

  • Exclude coverage for prepend conditional, ruby < 3.0 syntax for prepend

Version 6.4.2

  • Better module overrides in jsonapi
  • Replaced the is_a?(Hash) check for jsonapi reserved :page param with respond_to?(:Fetch) and prepended to the Frontend
  • Docs improvements and fixes

Version 6.4.1

  • Remove dependency on base64 (#618)Ruby 3.3 prints a warning if base64 is used without specifying it in the gemfile.
    Ruby 3.4 will error

Version 6.4.0

  • Implement JSON:API specifications
  • Added simpler nav generation, triggered by setting the size variable to a positive Integer
  • Fix for pagy_calendar_app.ru

Version 6.3.0

  • Calendar improvements:
    • Added the :fit_time option to page_at and pagy_calendar_url_at methods. It avoids the OutOfRangeError by returning the first or last page
    • Added starting_time_for and page_offset_at feedback methods to the Calendar base class
    • Prepended the pagy_calendar_url_at to the Frontend and Backend
    • Added calendar showtime
  • Updated node modules (dev on node 20)
  • Updated Gemfile and fixed new rubocop complaints

Version 6.2.0

  • Add Belarusian locale (#567)
  • Reordered RubyMine tasks (fix #541)

Version 6.1.0

  • Add Vietnamese locale (#550)
  • Maintenance (docs, test, gems and node modules updates) fixes and improvements

Version 6.0.4

  • Updated gems and npm modules
  • fix: Extras::Trim - fix trimming first page (#516)
  • Fix for new rubocop

Version 6.0.3

  • Updated Gemfile and npm modules
  • Improved Danish translation (#502)

Version 6.0.2

  • Fix: foundation combo helper (#469)

Version 6.0.1

  • Fix for pagy_bootstrap_combo_nav_js disabled input in certain bootstrap versions
  • Fix for wrong module in standalone extra
  • Fix for broken links in code comments and docs

Version 6.0.0

Breaking changes

Removed support for the deprecation of 5.0:

  • The pagy_massage_params method: use the :params variable set to a lambda Proc that does the same (but per instance). See How to customize the params.
  • The activesupport core dependency is now an optional requirement if you use the calendar: you must add gem 'activesuport' to your Gemfile if your app doesn't use rails.
  • The plain Time objects in the :period variable: use only ActiveSupport::TimeWithZone objects.
  • The :offset variable used by the Pagy::Calendar::Week: set the Date.beginning_of_week variable to the symbol of the first day of the week (e.g. Date.beginning_of_week = :sunday). Notice the default is :monday consistently with the ISO-8601 standard (and Rails).
  • The Pagy::DEFAULT[:elasticsearch_rails_search_method]: use Pagy::DEFAULT[:elasticsearch_rails_pagy_search] instead.
  • The Pagy::DEFAULT[:searchkick_search_method]: use Pagy::DEFAULT[:searchkick_pagy_search] instead.
  • The Pagy::DEFAULT[:meilisearch_search_method]: use Pagy::DEFAULT[:meilisearch_pagy_search] instead.

Suggestions for a smooth removal of deprecations from pagy 5

  • Upgrade to the latest version of pagy 5 (5.10.1)
  • Run your tests or app
  • Check the log for any deprecations message starting with '[PAGY WARNING]'
  • Update your code as indicated by the messages
  • Ensure that the log is now free from warnings
  • If you are overriding the pagy_url_for method ensure you add the extra parameters required..
  • Upgrade to pagy 6

Changes

  • Added new doc changes
  • Replaced the gitter support with the Q&A discussion links
  • reorganized cypress tests
  • Updated run configurations
  • Fix for bump.sh
  • Add Meilisearch finite pagination by default in the meilisearch extra (#417)
  • Allowing users to override the request path (#404)(squashed)
  • Fix the args in the Pagy::Countless#series override (#411)(fixes #410)
  • update: pagy_bare_rails script: for db testing (#379)
  • Fix rubocop glitch
  • added specific typescript installation to pagy-ci
  • Chore: update github action versions to latest
  • Reorganized pnpm setup and updated pagy.js to the new parcel default
  • Reduced the error level for eslint rules
  • Add Dependabot for GitHub Actions (#392)
  • Fix NoMethodError when page param is hash (#390)
  • Removed deprecated variables for the search extras
  • Removed deprecated :offset variable for Calendar::Week
  • Removed deprecated support for Time instances
  • Removed activesupport hard dependency
  • Removed support for deprecated pagy_massage_params
  • fixup .gitignore
  • Dropped docker env support
  • Calendar code cleanup and normalization
  • Implementation of calendar extra feature: pagy_calendar_url_at; refactoring and testing of the calendars files
  • Pagy::Calendar::*: Simpler code and improved readability
  • Added Pagy::Calendar#page_for(time) helper
  • Fix for ignored pnpm files from other branch
  • Updated cypress action
  • Updated node modules and cypress 12
  • Updated gems and rubocop config
  • Remove aria-label="pager" (#412)
  • Update korean translation (#413)
  • Fixed small typo in German (#409)
  • fix: Added Norwegian Nynorsk translation (#408)* Added Norwegian Nynorsk translation
  • Fix Pagy configuration and documentation for renamed "pagy/extras/shared" to "pagy/extras/frontend_helpers" (#399)
  • update link: to referenced rails apps (#384)
  • Added reference to avoid N+1 queries when using includes (closes #378)
  • Fix for broken links in docs
  • Added note about overriding the pagy_headers method in the headers extra
  • Bundle and npm updates
  • Add: documentation changes (#376)
  • Added post link in README; fix for another example in how-to.md
  • Fix for example in how-to.md (closes #373)
  • Added missing info
  • Fix: docs - typo, minimise ambiguity (#372)

Version 5.10.1

  • Changed default for :meilisearch_search method to always defined :ms_search method

Version 5.10.0

  • Adjusted Psych::VERSION condition to avoid deprecations
  • Improvement of search extras: (closes #367) (closes #369)
    • Added DEFAULT[:*_search] variables used to call the original :search method
    • Deprecated and renamed DEFAULT[:_search_method] to DEFAULT[:_pagy_search]
    • Updated config, docs and CHANGELOG deprecations
  • Fix for wrong calculation of :page in Pagy.new_from_meilisearch (closes #368)
  • Faster assignment of javascript path
  • Simplified webpack config, added rollup config and normalized doc
  • Better javascript doc (closes #365)
  • Added specific webpack javascript configuration info; a few minor doc adjustments
  • Added specific webpack javascript configuration info
  • Update javascript.md (#362)

Version 5.9.3

  • Fix for too generalized html_escape in pagy_url_for. (closes #363)
  • Docs updates

Version 5.9.2

  • Added info about using pagy-module.js with importmap
  • Bundle and npm update
  • Fix for non escaped query strings in links
  • Small improvements in src files
  • Added legacy methods to use the javascript files
  • Added missing require in docs
  • Updated docs with the proper way to use pagy with esbuild
  • Docs improvements
  • Improved the javascript files docs and moved into the lib/javascripts/README.md file

Version 5.9.1

  • Updated build.sh to set window.Pagy in pagy-dev.js
  • Update pagy.md (#360)
  • Minor - fix syntax highlighting (#361)
  • Updated info about the current way to use pagy.js with jsbuilder-rails/esbundle

Version 5.9.0

  • Npm and bundle update
  • Fix and refactoring of pagy calendar calculation of local time (closes #358):
    • The Pagy::Calendar::* classes require ActiveSupport (temporarily added activesupport dependency that will e replaced in 6.0)
    • The :period items can be Time or TimeWithZone objects (which work across DST ranges)
    • Deprecated use of :offset to set the first weekday, in favour of Date.beginning_of_week (default to :monday)
    • Added DST specific tests
    • Fix for calculation of label quarter
    • Updated docs and tests
  • Fix for wrong offset in gearbox extra (closes #356)
  • Reduced the public interface of pagy.js and relative files to just version and init()
  • Add Ruby 3.1 to CI (#354)

Version 5.8.1

  • Shorter warning messages and minified pagy.js
  • Fix for wrong decoding of non latin charsets

Version 5.8.0

  • The JSON in data-pagy attributes is base64 encoded: it is smaller than HTML escaped and it avoids crawlers to follow look-alike links
  • Removed redundant PagyJSON type

Version 5.7.6

  • Updated Gemfile.lock and package-lock.json
  • Broader browserslist and better doc for javascript
  • Updated build.sh
  • Improvement of pagy.ts:
    • Moved warn() out of init()
    • More inclusive try block
    • Simplified rjsObserver function
    • Replaced getOwnPropertyNames with keys
  • Fix for missing entry in CHANGELOG.md

Version 5.7.5

  • Fix for unwanted offset in source map data

Version 5.7.4

  • Added parcel-plugin-nuke-dist
  • Renamed pagy.mjs -> pagy-module.js; added TypeScript declaration file and build.sh

Version 5.7.3

  • npm update
  • Simper pagy-dev.js; added pagy.mjs module
  • Updated .eslintrc.json and package.json files

Version 5.7.2

  • Simplified keyword naming
  • Better compilation and development support for pagy.js. Replaced babel with parcel.

Version 5.7.1

  • Simpler syntax for nav js helpers
  • Added input e2e test file
  • Simpler abstraction of input based js helpers

Version 5.7.0

  • Bundle and npm update
  • TypeScript/Javascript improvements:
    • More efficient handling of responsive *nav_js helpers
    • Replaced window resize event listener with ResizeObserver
    • More robust input based js helpers (catching possible invalid user input)
    • Improved typing, function naming
    • Added error catching

Version 5.6.10

  • Improvement and fixes for client-side related structure
  • Replaced e2e/cy script with a couple of npm scripts backed by the start-server-and-test
  • Added trim test for items_selector_js; cleanup of e2e dir
  • Normalized json files indentation
  • Added trim to e2e tests
  • Reorganized cypress tests: faster and more accurate
  • Simpler pagy.ts

Version 5.6.9

  • Better doc for e2e related commands in docker/README.md
  • Updated package-lock.json and improved ci-cache.sh
  • Used better syntax and code style in typescript files
  • Updated copyright year
  • Refactoring of typescript related files and dirs:
    • Moved e2e dir in the project root
    • Added typescript linting plugins and configuration
    • Converted all the e2e test to typescript
  • Updated Docker, VSC and .idea configurations

Version 5.6.8

  • Reorganized TypeScript/JavaScript with npm workspaces and better script naming
  • Fix typo in gemspec
  • Better typescript configuration with source maps and declarations

Version 5.6.7

  • GitHub Actions: added quotes to version ruby 3.0
  • Added typescript + babel environment for better pagy.js
  • Updated e2e environment
  • Updated gemfiles
  • Fix for support doc snippets and other typos
  • Minor update of gemfiles and docs
  • Added more gemspec metadata entries (closes #351)

Version 5.6.6

  • Docs improvements
  • Added **_ to series and sequels
  • Updated rematch and gemfiles
  • Updated release-gem.yml workflow

Version 5.6.5

  • Improved a few code comments; added post link in README
  • Full name for translate, aliased as t
  • Added check for no calendar units in pagy_calendar configuration
  • Updated gemfiles
  • Small docs layout adjustment

Version 5.6.4

  • Updated RM run configurations
  • Fix for missing innerHTML reset, unintentionally committed during a cleanup (closes #350)
  • Updated gemfiles, docs and comments

Version 5.6.3

  • Improved readability and efficiency of calendar files
  • Fix for English spelling in local variable name

Version 5.6.2

  • Updated pagy.manifest for Tamil locale
  • Add Tamil (ta) translation (#349)
  • Internal changes in calendar files:
    • Simpler calculations for month mixin
    • Normalized naming of non-api methods
    • Better comments
  • Pagy::I18n: small performance improvement
  • Docs reorganization
  • Update paths_ignore for skip CI and docs

Version 5.6.1

  • Updated cypress and bundler
  • Improved efficiency of unit labelling and support for custom calendar unit sub formats
  • Added missing initializer default for calendar quarter and missing doc for custom units

Version 5.6.0

  • Updated gemfiles
  • Updated docs
  • Added calendar quarter tests
  • Internal calendar refactoring to allow custom units; added quarter unit

Version 5.5.1

  • Docs updates
  • upgrade bootstrap template navs: call pagy_link_proc with link_extra key (#348)
  • Renamed internal #time_for -> #start_for
  • Docs fixes and improvements

Version 5.5.0

  • Updated cypress and related packages
  • Calendar API: FINAL breaking changes (stable from now on):
    • Refactoring of calendar classes and variables:
    • Moved calendar defaults from Pagy::DEFAULT to class-specific Pagy::Calendar::*::DEFAULT
    • Renamed variables:
      • :minmax -> :period
      • :time_order -> :order
      • :week_offset -> :offset
      • :*_format -> :format
    • Returning local time instead of UTC time for the utc accessors, now renamed:
      • #utc_from -> #from (use from.utc if you need it)
      • #utc_to -> #to (use to.utc if you need it)
    • Inverted the logic for the :skip key in the #pagy_calendar conf, now renamed:
      • :skip -> :active
    • Renamed methods:
      • #pagy_calendar_minmax -> #pagy_calendar_period
      • #pagy_calendar_filtered -> #pagy_calendar_filter
    • Added alternative way to delegate the localization to i18n without the i18n extra
    • Updated pagy_calendar_app.ru
    • Fix for wrong reordering in :desc order
    • Documentation fixes and improvements
    • Removed the warning for the API changes: the API is stable after these changes

Version 5.4.0

  • Calendar API breaking changes for refactoring of Pagy::Calendar and calendar extra:
    • Added complete compatibility with all the backend extras
    • Simpler usage with automatic handling of pagy objects
    • Less variables and simpler requirements for the methods to implement
  • Updated gemfiles
  • The localize method overridden by the i18n extra must receive a format
  • Series and sequels use keyword arguments and pagy_*nav methods accepts a size keyword argument
  • Docs improvements
  • Removed unnecessary empty section in calendar docs
  • Fixes for typos and misalignment

Version 5.3.1

  • Added screenshot to the calendar extra (closes #346)
  • Added bump.sh script to bump the version in multiple files; check for consistency and optionally commit the changes
  • Minor fixes
  • Changelog improvements
  • Reversed CHANGELOG (closes #345)
  • Calendar I18n small internal renaming and docs improvements

Version 5.3.0

  • Implemented localization of time labels through the i18n extra delegation
  • Renamed internal module and files of SharedExtra to FrontendHelpers
  • Added support for *nav_js to Calendar
  • Simplified page labelling, moved into the pagy classes and removed frontend methods
  • Deprecated pagy_massage_params: use the :params variable set to a Proc that does the same, but per instance
  • Added apps README
  • Completed overflow fix for pagy Countless

Version 5.2.3

  • Fix for overflow :empty_page in regular Pagy instances, not returning an empty page
  • docs: add tutorial, simplify header (#343)
  • Refactoring of rails_inline_input.rb (include and close #342)

Version 5.2.2

  • Fix for missing defined?(Calendar) checks; small simplification in headers extra
  • Calendar docs improvements and fixes

Version 5.2.1

  • Reorganization of mock collection classes; enabled rubocop layout in tests
  • Small refactoring of the overview extra
  • A few improvements for the Calendar pagination; added the current_page_label method

Version 5.2.0

  • Small changes in code; updated gemfiles, tests and docs
  • Implemented calendar extra to paginate a Time periods by unit (year, month, week or day)
  • Added pagy_labeler frontend method overridable for changing the link text from a simple page number to any arbitrary string
  • Enabled rubocop Style/Documentation cop
  • Updated npm modules and gemfiles
  • Docker better mounts
  • Used gem-generic release-gem action

Version 5.1.3

  • Added single action standalone rails_inline_output.rb
  • Small improvements in code and docs
  • Fix for rails problem with internal params in pagy URL (closes #341)
  • Documentation improvements
  • A few details tag in the README should be opened by default

Version 5.1.2

  • Refactoring of pagy_url_for and relative test:
    • Fix for ignoring the items_extra variable
    • Replaced request.GET with request.params to enable POST pagination
    • Refactoring of Mock test classes for better handling of params

Version 5.1.1

  • This reverts commit 1d77e672d5b7813108b40c13ca93fdec045f4c03.
    Generating the URL by using the application params method breaks rails apps because it requires manual changes in the apps.

Version 5.1.0

  • Fix and refactoring of pagy_url_for and relative test:
    • Fix for ignoring the params not coming from the request
    • Fix for ignoring the items_extra variable
    • Refactoring of Mock test classes for better handling of params
  • Improved code comments, formatting and docs fixes
  • Countless extra: simplified code, internal renaming of locals and docs update

Version 5.0.1

  • Updated docs and issue templates
  • Added cypress-dark theme to e2e test
  • Refactoring of coverage to include 100% of line and condition branches covered
  • Simplification of metadata extra
  • Refactoring of exceptions
  • Added CHANGELOG_LEGACY

Version 5.0.0

Breaking changes - 1. Code update

Pagy 4 dropped the compatibility for old ruby versions >2.5 and started to refactor the code using more modern syntax and paradigms and better performance. It deprecated the legacy ones, printing deprecation warnings and upgrading instruction in the log, but still supporting its legacy API. Pagy 5.0.0 cleans up and removes all that transitional support code.

The changes for upgrading your app cannot be fixed with simple search and replace, but fear not! Fixing them should just take a few minutes with the following steps:

  • Upgrade to the latest version of pagy 4
  • Run your tests or app
  • Check the log for any deprecations message starting with '[PAGY WARNING]'
  • Update your code as indicated by the messages
  • Ensure that the log is now free from warnings
  • Upgrade to pagy 5

FYI: Here is the list of the deprecations that are not supported anymore:

Removed support for deprecated variables

  • Pagy::VARS[:anchor] is now Pagy::DEFAULT[:fragment]

Removed support for deprecated arguments order

  • The argument order in pagy_url_for(page, pagy) is now inverted: pagy_url_for(pagy, page)

Removed support for deprecated positional arguments

The following optional positional arguments are passed with keywords arguments in all the pagy helpers:

  • The id html attribute string with the id keyword
  • The url|absolute flag with the absolute keyword
  • The item_name string with the item_name keyword
  • The extra|link_extra string with the link_extra keyword
  • The text string with the text keyword

Breaking changes - 2. Simple search and replace

There are a few renaming that have not been deprecated in previous versions because they are extremely easy to fix with simple search and replace (while implementing deprecations would have been detrimental to performance and complex for no reason)

Consistency renaming

A few elements have been renamed: you code may or may not contain them. Just search and replace the following strings:

  • Rename Pagy::VARS to Pagy::DEFAULT
  • Rename enable_items_extra to items_extra
  • Rename enable_trim_extra to trim_extra
  • Rename Pagy::Helpers to Pagy::UrlHelpers
  • Rename pagy_get_params to pagy_massage_params

Items accessor

The items accessor does not adjust for the actual items in the last page anymore. This should not affect normal usage, so you can ignore this change unless you build something on that assumption.

If your code is relying on the actual number of items in the page, then just replace @pagy.items with @pagy.in wherever you meant that.

FYI: The @pagy.items is now always equal to @pagy.vars[:items] (i.e. the requested items), while the @pagy.in returns the actual items in the page (which could be less than the items when the page is the last page)

Changes

  • Removed support for deprecations
  • Refactoring of Pagy and Pagy::Countless classes, I18n, and url helpers
  • Refactoring of the docker environment, addition of ready to use VSCode setup
  • Changed general module structure (use of prepend instead of re-opening modules)
  • Added gearbox extra for geared pagination
  • Added configuration files for a full working VSCode devcontainer environment
  • Added Run Configurations for RubyMine
  • Improved the usage of e2e tests
  • Updated doc, gemfiles and github workflow
  • Other minor fixes and improvements in code and doc

Version 4.11.0

Changes

  • added countless_minimal feature
  • added missing deprecation to countless class
  • doc improvements

Commits

  • 5103ee9: added countless_minimal feature
  • ed6bc0b: added missing deprecation to countless class
  • bedb2d6: Fix - link directly to docs [ci-skip] (#328)
  • 74aa300: Remove minor extra parenthesis in trim docs [ci-skip] (#326)
  • f8b96eb: docs - add links directly to docs page [ci-skip] (#324)

Version 4.10.2

Changes

  • improve the customization of url when using the trim extra
  • better tailwind rules
  • doc fixes and improvements

Commits

  • 9e7fd48: improve the customization of url when using the trim extra (closes #325)
  • 0d48b5f: better handling of extra docker-compose run files
  • f5ed8f6: added note about the possible workaround for bundler/inline bug
  • 2715660: doc fixes
  • 7187101: Javascript doc improvements
  • 24b7e78: release workflow refactoring
  • 20d1d8e: chore: add github action - publish & tag versions (#322)
  • 922fd7f: better tailwind rules
  • 28983e6: added doc for using Searchkick.pagy_search (closes #319)

Version 4.10.1

Changes

  • fixes for meilisearch extra

Commits

  • ec748c2: Fixes for Meilisearch extra (#318)

Version 4.10.0

Changes

  • added arabic locale

Commits

  • 9bc548e: added missing keys for Arabic (#317) (closes #295)
  • 1b95674: Add arabic locale, pluralization and tests; alpha ordered tests

Version 4.9.0

Changes

  • added meilisearch extra
  • docs fixes

Commits

  • 572ed78: updated pagy-ci.yml
  • 8fe1d19: Fix documents for searchkick (#315)
  • 744717f: Meilisearch extra (#316)
  • fb65260: updated issue templates
  • 1c3d283: Added Pagy::Console links to README

Version 4.8.1

Changes

  • fix for deprecation and test improvements

Commits

  • 0f1a7f2: improved deprecation_tests (see #311)
  • 7e9c9cd: fix(pagy): deprecated_var is a class method (#311)

Version 4.8.0

Changes

  • Lowered minimal ruby version requirement to 2.5+
  • Simpler tests using the rematch gem
  • Docker environment improvements

Commits

  • b5b500a: Merge branch 'backport-to-2.5' into dev
  • 001f22f: fix for different handling of module prepend in ruby <3
  • d6d71a5: fix for frozen error handled differently on ruby <3
  • 8a2cf76: replaced syntax not available in ruby 2.5
  • 14b3a0c: lowered the minimum ruby version requirement to 2.5
  • 8c76943: updated Gemfile.lock
  • 934f602: added comment in doc example about the inverted order of arguments in previous versions
  • acd59f5: moved command out of default docker-compose to its own file in order to avoid error before first bundle install
  • 527a14f: Gemfile update
  • 842de05: better tests and stricter rubocop
  • d82e95c: Gemfile updated and used for CI
  • 675f587: Gemfile: added gemspec and updated rematch
  • b0a6603: Updated to rematch 1.0 and copyright year
  • 8bda6a9: added rematch gem and reset rematch stores
  • f62fa88: added e2e tests to ci
  • 04755aa: updated puma
  • 4ec03f1: added HTML validation for all the helpers and styles
  • 87b2f62: refactoring e2e: replaced snapshot plugin; updated to cypress 7.3.0

Version 4.7.1

Changes

  • Fixed a couple of HTML validity issues with uikit and materialize combo_nav_js
  • Improved rematch testing
  • Updated the documentation

Commits

  • 8cb4193: fixed and simplified uikit combo nav
  • f762aca: used chip class for materialized combo nav
  • 064a2b5: rematched tests
  • 2a45321: expanded the combo nav label to the whole text
  • 37c65a8: fixed and simplified materialize combo nav
  • 789773b: removed faulty PAGY_REMATCH variable function; added rematch_all rake task
  • eb2df6d: updated docs [skip ci]

Version 4.7.0

Changes

  • Refactoring of pagy-json tags into data attributes
  • Added the rematch testing system to ease maintenance of tests
  • Added Ukrainian locale
  • updated Gemfile and doc

Commits

  • 887c783: better doc for E2E testing
  • 738fd93: updated manifest
  • 0110e3a: Ukrainian locale (#310)
  • 9265cd2: extended must_rematch to other tests
  • d217965: refactoring of pagy_json_tag to pagy_json_attr: from script tag to data attribute
  • 57e6f91: added rematch test to ease maintenance of tests
  • 94561a0: updated puma

Version 4.6.0

Changes

  • Added labels to all input tags for improved usability and compliance
  • Fixed a few HTML typos and redundant or illegal attributes
  • Improved Sinatra app usable for experiment with pagy and providing support for ode issues
  • Documentation improvements
  • Added E2E testing for all pagy helpers
  • GitHub management improvements

Commits

  • 0fe2e49: added helpers and navbar tests for all the styles; updated docker readme
  • 26b57a8: Added fix for Cypress bug affecting the snapshots plugin, improved cypress dockerfiles; added cypress studio and basic try
  • 7693b3e: cypress: updated images; better open-cypress.yml with persistent configuration settings; added snapshots plugin
  • 94e262f: added label tag to the pagy inputs
  • f5e3482: removed redundant role="navigation" attribute from nav tags; removed illegal aria-label="pager" from semantic div
  • 0467d32: fixes for html typos in semantic, foundation and materialize helpers
  • 3f2c9f0: doc small fixes
  • bff8c5f: refactoring of sinatra apps; added quick-start doc section
  • b0baaa5: simplified standalone_app.ru
  • 5442b34: fix broken link [ci skip]
  • 17b1faf: improved issue template [ci skip]
  • f933cd7: Added issue template and standalone_app to use as a scaffold for reproducing issues [ci skip]
  • c8e41e3: added superails screencast; resized thumbnails [skip ci]
  • 6116c32: added a few references in the how_to doc [skip ci]

Version 4.5.1

Changes

  • Better Pagy::Console usability

Commits

  • 4ee8bbf: pagy console extracted in its own module

Version 4.5.0

Changes

  • Added E2E infrastructure for development and testing
  • Refactoring of Javascript and docker environment

Commits

  • 1b7b014: Add: clarity, further instructions to run pagy tests [skip ci] (#304)
  • 7c723ba: added basic structure to run cypress tests and open the cypress IDE

Version 4.4.0

Changes

  • Passing positional arguments (besides @pagy) to all the helpers is deprecated and it will be supported only until pagy 5.0
  • All the helpers accept more optional keyword arguments variables, for example:
    • pagy*_nav(@pagy, id: 'my-id', link-extra: '...')
    • pagy*_nav_js(@pagy, id: 'my-id', link-extra: '...', steps: {...})
    • pagy*_combo_nav_js(@pagy, id: 'my-id', link-extra: '...')
    • pagy_items_selector_js(pagy, id: 'my-id', item_name: '...', i18n_key: '...', link_extra: '...')
    • pagy_info(@pagy, id: 'my-id', item_name: '...', i18n_key: '...')
    • pagy_prev_link(@pagy, text: '...', link_extra: '...')
    • pagy_next_link(@pagy, text: '...', link_extra: '...')
    • pagy_link_proc(@pagy, link_extra: '...')
    • pagy_url_for(pagy, page, absolute: nil) (notice the inverted page/pagy order with the legacypagy_url_for(page, pagy, url=nil))
  • deprecated :anchor variable in favor of :fragment
  • Refactoring of rake tasks, tests, items extra, javascript helpers
  • Gems and documentation updates and fixes
  • Added standalone extra

Commits

  • b69a2f2: Merge branch 'dev'
  • 9611eac: updated CHANGELOG
  • d977767: better links in extras.md
  • aa83126: added standalone extra
  • e22c9c3: Merge branch 'args-refactoring' into dev
  • d791a04: deprecated :anchor variable in favor of :fragment
  • e91e4da: [skip ci] update javascript documentation (#305)
  • 034afe8: config/pagy.rb: moved pagy variables block at the beginning
  • bd0bb6c: renamed :trim variable to the more explicit :enable_trim_extra; removed redundant tests
  • 76096e7: simplified the items extra, and added its features to the array and arel extras
  • 86fb20e: fixed pagy_semantic_nav hellips
  • 2382482: added link_extra keyword arg to pagy_items_selector_js
  • 627a0dc: also pagy_link_proc uses keyword arguments
  • 8ff2665: completed args conversion also for pagy_url_for and pagy_metadata
  • 6a8fbb4: added "pagy-info" and "pagy-items-selector-js" classes to helpers output
  • ad350e1: pagy_info wrapped into span tag, and added id keyword arg
  • 2faca63: small improvement to pagy_url_for
  • 2acbac6: added "pagy-njs" class to all pagy*_nav_js helpers
  • 670f5a5: updated documentation
  • 64347f8: changed the argument order from pagy_url_for(page, pagy) > pagy_url_for(pagy, page) for consistency with all the other helpers; deprecation warning and support for pagy < 5
  • 393d216: updated gems
  • d2891fb: refactoring and additions of tests
  • 1538f24: updated tests with keyword arguments
  • 6ce0c1d: helpers: deprecated positional id argument and added keyword arguments
  • 9fd7930: removed id method and added id arg to the pagy*_nav helpers
  • ec74e4f: removed the mandatory id arg passed to pagy_json_tag; updated tests; simplified pagy.js using previousSibling
  • 3f13014: added resource post and relative link [skip ci]
  • 7927f37: added manifest:check to CI for master and dev and to the default rake task
  • 01f8f1c: refactoring of Rakefile now split in different task files

Version 4.3.0

Changes

  • Improved efficiency of #series algorithm
  • Added new features to the trim extra
  • Added rubocop compliance (additional rubocop-packaging plugin)
  • Many test tasks improvements
  • CI transition from Travis to Github Action

Commits

  • 80cef45: updated Gemfile
  • 3cc464b: added coverage_summary at the end of the default task; added possibility of creating a coverage report
  • 0424b71: docs and comment additions/fixes [skip ci]
  • b6115e7: Added explicit pagy.manifest to comply with rubocop-packaging (see also #297)
  • ef1980e: refactoring of CI from Travis to Github Actions
  • 9fb1195: more verbose condition
  • fe565eb: removed RUN_RUBOCOP
  • 60be542: fix missing codecov formatter/circular reference problem
  • 8c4ccfc: rubocop: added rubocop-packaging plugin; expanded inclusion of files
  • 487eb70: small code normalization
  • 3ea2d27: codecov gets already required in travis on ruby 3.0 (but not locally)
  • e17d294: improved series algorithm efficiency (now up to ~5x faster and ~2.3x lighter than the previous one)
  • 1051e6d: trim feature: added :trim variable to opt-out to trim even with the trim extra loaded, or invert the logic to out-in by setting Pagy::VARS[:trim] to false
  • ece4daa: minor code restyling
  • 0e3a09d: added :ide_development bundle group; small README changes
  • 5e6f749: simpler test tasks
  • e5ee4e7: CI: Remove Bundler 1.x workaround (#296)The "github" source now has that form, already.

Version 4.2.0

Changes

  • Fix for Pagy::Frontend::I18n conflicting with rthe I18n gem namespace after inclusion and safer renaming of other modules
  • Fix the simplecov setup reporting less coverage than what's actually covered
  • Updated Tailwind styles
  • Included the test dir in the coverage check, refactoring and additions of tests
  • Big code restyling following ruby 3.0 syntax and cops; tried to make the code simpler, more readable and verbose with almost negligible performance loss.

Commits

  • a62ae94: Updated README
  • 9efb908: removed ENABLE_OJ from tests and travis config
  • dede255: removed RUN_SIMPLECOV option
  • fc614bd: fix/refactoring for simplecov setup reporting less coverage than what's actually covered
  • 5e6ecf1: refactoring of Rakefile
  • aea25a1: code-restyling: test
  • cb67353: code-restyling: extras
  • b2dc35a: code-restyling: locales, config
  • b638764: code-restyling: root files and Pagy Core
  • aac54aa: Renaming prepended modules with more specific and safe naming convention (see Issue #290 PR #293)
  • 3eef450: Simpler test for I18n namespace conflict
  • b2aaa01: README: fix typo (#289) [ci-skip]
  • 3a9e70f: Fix extra i18n namespacing (#293)* Add a test that looks at i18n working in Modules included (fixes #290)
  • 8cd25d2: renamed docker > pagy-on-docker
  • a8060b1: fixed doc typos
  • f0e0d41: removed require for minitest/reporters
  • c178c6f: docker README fixes [ci-skip]
  • ac988fd: docs fixes
  • 4d20d34: Update Tailwind styles (#285)
  • 9a14400: Small README addition [ci-skip]
  • 9b40cdd: Update frontend.md (#284) [ci-skip]
  • 2bbf204: added Mike Rogers' screencast [ci-skip]
  • b24b86d: Added note for brakeman false positive warnings (closes #243) [ci-skip]
  • df3c8d6: small syntax normalization
  • 618d226: docker README fix [ci-skip]

Version 4.1.0

Changes

  • added Bosnian, Croatian and Serbian locales

Commits

  • 8da0e06: Add Serbian locale (#283)
  • dcfc659: Add locales for Bosnian and Croatian language (#281)

Version 4.0.0

Breaking Changes

  • renamed Pagy::Search as Pagy::Searchkick or Pagy::ElasticsearchRails

Changes

  • Dropped support for all jruby versions and ruby version <3.0 (use pagy 3.x instead)
  • Dropped deprecation for old/renamed locales
  • Fixed error in searchkick extra with ruby 3
  • Various refactorings taking advantage of the dropped support for old ruby versions

Commits

  • 4d48d33: refactoring of pagy_json_tag
  • 745e68f: minor fixes for docker
  • 296f060: added pagy_search name customization to elasticsearch extras
  • 98a5254: refactoring of the elasticsearch_rails extra matching the actual params of the search method
  • 6966fe3: refactoring of elasticsearch extras
  • aecb565: normalization of i18n naming
  • b3f78b8: refactoring of items extra using module prepend instead alias method chaining and module_eval; refactoring of tests (countless was masking failures of elasticsearch extras)
  • 8ec7893: refactoring of trim extra using module prepend instead alias method chaining
  • 05d9e92: used ruby 3.0 syntax
  • f1d89a5: refactoring of I18n extra using module prepend instead alias method chaining
  • df8607e: refactoring of overflow extra using module prepend instead alias method chaining
  • c361293: Gemfile: uncommented performance group
  • 66e29d1: fixes for the docker README
  • 2a343ce: added README note about versions
  • dadd2f1: ruby 3.0 syntax
  • ce1d7ec: removed legacy I18n compatibility
  • 6751412: added a few env variables in order to control the verbosity of tests; added test for negative page number
  • 3eac56b: removed unused Hash additional methods
  • 24ab5c6: simpler travis setup
  • ba100dc: updated documentation
  • 5d53043: emptied CHANGELOG and dropped LEGACY_CHANGELOG
  • 33c467a: removed MARK constant
  • 49040fb: removed EMPTY constant used for legacy compatibility with frozen_string_literal
  • b826f61: updated rubocop and fixed offending lines
  • f075337: removed deprecated locales
  • a70881a: updated version to 4.0.0
  • 4676788: updated docker environment for pagy 4
  • 4a85efe: updated gemfile and gemspec for pagy 4
  • fcaab88: updated gemfile and gemspec
  • 467b985: added docker development environment

Version 3.11.0

Changes

  • added Swahili localization

Commits

  • fbc322f: Add Swahili (sw) to pagy (#276)
  • ebbac6b: Fix: broken links (#272) [ci-skip]
  • 2178436: restored file removed by mistake

Version 3.10.0

Changes

  • added Czeck localization

Commits

  • 316183f: minor adjustments to p11n.rb; removed local file
  • 2e7fa1b: Update with 'cs' (#269)added lambda for 'west_slavic' and 'hash for 'cs' language
  • 70760c6: Czech localization (#268)

Version 3.9.0

Changes

  • added link tags helpers to support extra

Commits

  • a66491b: added link tags helpers to support extra
  • 2ef08a9: @pagy_locale explicitly initialized to avoid warning
  • 261c6a1: reorganized gemfiles for travis, removed jruby 9.2 tests and added CAVEATS and removed redundant ||=nil
  • 619d7d1: updated gems
  • fc861d8: added .gitattributes to fix github linguist [ci-skip]
  • 287b94f: added Raul Palacio screencast link to README
  • 52c65a3: added "Contributions" section in the README
  • f09a80a: Add tutorial link to readme (#259)
  • 86bf3dc: Fix: typo [ci-skip] (#257)
  • bd3bcc7: Update copyright notice to 2020 (#256) [ci-skip]
  • 01e4a14: [ci-skip] Add note re: html safe helpers (#254)Fixes discussion noted in: #6
  • 1d812be: doc improvements [ci-skip]

Version 3.8.3

Changes

  • Portuguese translation
  • Fixes and documentation improvements

Commits

  • f64a1d6: added pagy reference to jetbrains link
  • 8a4596f: Add translation for Portuguese (#250) [ci-skip]
  • 515de9b: Adds parentheses (#249) [ci-skip]
  • 734f30c: added basic sinatra app
  • c7aa44d: updated gems
  • 62bd4ac: added tailwind extra styles
  • c2e8eef: added "Customizing CSS styles" how to section
  • 614a3e1: fix for extra "-js" in "pagy-combo-nav-js"
  • 8673ca4: a couple of how_to additions

Version 3.8.2

Changes

  • Fixes, improvements and gems updates

Commits

  • a1a7962: gems update
  • 0a70110: item_name can be overridden by passing an already pluralized string to pagy_info
  • 28913d6: Use proper attributes for "previous" links (#244)

Version 3.8.1

Changes

  • Various fixes ad gems update

Commits

  • 1aa1fa7: gems update
  • 289d38a: added conditional definition of pagy_t_with_i18n to avoid ruby 2.7 "last argument as keyword parameters" deprecation warning (closes #241)
  • d571719: added raw_response method check for pagy_elasticsearch_rails (#234)
  • 7a09bb9: fix typo in documentation
  • d7a9f7a: Fixes boundary character escaping in regex in js trim function (#239)
  • 3561885: gems update

Version 3.8.0

Changes

  • Khmer translation
  • Added support for elasticsearch < 6
  • Documentation fixes

Commits

  • bba1bca: used ternary conditional
  • a9cee7d: Support ES5 on elasticsearch rails (#234)
  • d58a39d: Khmer translation for pagy (#235)
  • 7ced96c: fix for missing suffix fr method reference in doc
  • 73600be: improved consistency in searchkick and elasticsearch_rails documentation examples (#231)

Version 3.7.5

Changes

  • Danish translation
  • Re-enabled legacy jruby travis config
  • Added ruby 2.7 travis testing and rubocop config

Commits

  • 721d01f: more documentation fixes and improvements
  • 246fca7: Danish translation for Pagy (#232)
  • e57215f: added ruby 2.7 travis testing and rubocop target version
  • 280164a: re-enabled travis testing for old jruby (travis fixed the jruby images issue)

Version 3.7.4

Changes

  • Locales normalization and deprecations
  • Bundle update

Commits

  • c424466: Merge branch 'locales-normalization' into dev
  • 032615c: added Pagy::I18n deprecation tests
  • ebd6151: added deprecation remapping
  • e1bd70c: renamed pt-br -> pt-BR
  • a77dfcf: fix for sv and sv-SE pluralization
  • 6a31db6: Swedish locale: add sv-SE, rename se -> sv (#226)
  • 00bd312: bundle update (closes #208, closes #223, closes #224)
  • 133da16: Add tutorial link (#222) [ci skip]

Version 3.7.3

Commits

  • a2b915e: reorganization of Javascript documentation
  • fead55c: Remove duplicated pagination class for Bootstrap (#212)

Version 3.7.2

Changes

  • fix for numeric headers in the headers extra

Commits

  • 6a93b97: headers must be strings (closes #211)

Version 3.7.1

Changes

  • fix for arel count

Commits

  • cbe4a97: Type cast pagy_arel_count value to integer (#207)
  • 973f4fe: updated link [ci skip]

Version 3.7.0

Changes

  • updated French and Spanish translations
  • added Italian translation
  • internal renaming Pagy::Frontend::MARK > Pagy::PAGE_PLACEHOLDER

Commits

  • 434912b: updated rubyzip gem
  • b627e55: add italian translation (#202)
  • 3341dcc: created ITEMS_PLACEHOLDER constant
  • 7775155: replaced Pagy::Frontend::MARK with Pagy::PAGE_PLACEHOLDER (MARK is still defined for backward compatibility)
  • 439676e: Fixed previous translation for Spanish (#199)
  • ee1b1f0: Update fr.yml (#195)
  • 8b6f81f: added pagy-cursor link in README
  • c290dca: update incorrect tutorial link (#198)
  • 77c045d: add: tutorial link in ReadMe - complex search forms (#194)
  • 846eece: fix in README.md
  • 224748a: fix for resurrected-by-mistake line in README

Version 3.6.0

Changes

  • added arel extra
  • updated Gemfile for development environment
  • docs fixes and improvements

Commits

  • af0f189: updated minitest and minitest-reporter gems and removed deprecation
  • 6a62de5: updated Gemfile
  • 4856782: Arel Extra (#189)
  • 2a2d9b0: Revert "Added digital-climate-strike banner"This reverts commit 07f7d9256ef9b1cef1c1eaf6da56ebb03daa19d5.
  • b5c2809: docs fixes
  • f8e4f42: replaced markdown file link for documentation link in extras
  • 6a5c271: updated github-pages gem
  • 60d243c: small doc improvement [ci skip]
  • 2feb451: docs changes
  • 07f7d92: Added digital-climate-strike banner
  • 281da85: update grammar (#187)
  • 739f032: Update how-to: Correct to require header for API (#188)

Version 3.5.1

Changes

  • added support for elasticsearch 7

Commits

  • ca736cc: Adding elasticsearch7 support (#185) Fixes #184
  • 13b7314: correct typos in docs and initializer (#186) [ci skip]
  • 9734ed6: Docs style changes [ci skip]
  • fb8bcf4: doc fix for missing :partial key in rendering example (closes #180)
  • 7160a18: Minor docs changes [ci skip]

Version 3.5.0

Changes

  • Added Korean locale
  • Added Catalan locale
  • Added metadata extra
  • Docs fixes and improvements

Commits

  • 2813973: Docs:
    • added detailed Quick Start steps (closes #176, closes #177)
    • added specific sections for API clients and javascript frameworks
  • 43349a8: added metadata extra
  • 261e6e6: Add Catalan localization (#178)
  • 30ed505: temporarily removed jruby 1.7 and 9.0 rvm from travis - waiting travis to fix their rvms or suggest some work around see issue
  • 2863c55: added minitest-reporters gem
  • 0b42e5c: Add translation ko.yml for korean (#172)
  • da3fc37: minor doc fixes [ci skip]

Version 3.4.1

Changes

  • Improvements for searchkick extra
  • Added javascript Pagy.version

Commits

  • 0ac5c27: refactoring of pagy_search and its usage in order to allow optional term argument for searchkick (#169)
  • 389755b: small docs editing [ci skip]

Version 3.4.0

Changes

  • Added UIkit nav, nav_js and combo_js helpers and templates.

Commits

  • 1eb25ec: Implement UIkit pagination (#164)

Version 3.3.2

Changes

  • Fix for #163
  • Javascript improvements

Commits

  • aff8cbb: better handling of resize listeners; added render function to the pagy element
  • c699fe5: fix for javascript/turbolink nav.render firing when element not found (#163)

Version 3.3.1

Changes

Namespacing of all exceptions, still backward compatible with legacy rescues

Commits

  • 530cb24: all the exceptions are namespaced and can return more information for easier rescue of special cases

Version 3.3.0

Changes

  • Added Bulgarian locale

Commits

  • 2c56428: added pluralization comment to bg.yml dictionary
  • 7111cec: Bulgarian language yml file. (#162)
  • a46f6b5: improved suggestion for headers usage

Version 3.2.1

Commits

  • b403a35: improved trim regex: simpler, safer and more tested
  • bd3be09: normalized aliasing with alias_method
  • 3c60d9b: minor doc fixes
  • 9aa1bea: fix for missing "partial" in examples for template usage (#160)
  • 405b4d2: reorganization and renaming of test helpers
  • 459edaa: improved notes about preventing crawlers to follow look-alike links
  • 01c73d3: added search box to docs

Version 3.2.0

Commits

  • 2341a83: docs fixes and additions
  • 96ad00f: extended trim extra support to all the *_js helpers:
    • refactored trim extra
    • simplified MARK strings
    • use of RegExp patterns in pagy.js
    • added and improved tests

Version 3.1.0

Commits

  • 1732dee: refactoring of marker and hidden links in *_js helpers:
    • MARKER renamed to MARK is simpler, static and hardcoded in pagy.js as well
    • removed hidden links to bogus urls that were triggering crawlers to follow (#158)
    • pagy_items_selector now works also with trim and in combination with other *nav_js helpers
    • updated tests
  • aa140e1: doc improvement
  • d0e4ba6: a few fixes and doc improvements
  • c1ddbd9: Fix typo in docs example (#156)
  • e0b2a2b: removed duplication in LEGACY_CHANGELOG
  • ecb6822: fix typo in README

Version 3.0.0

Breaking Changes

Refactoring of i18n

  • The zh-cn and zh-hk were named incorrectly. They have been properly renamed as zh-CN and zh-HK. If you used them, you should update the Pagy::I18n.load statement.
  • Removed the plural keys inconsistent with the locale: now the dictionary files are 100% compatible with the I18n gem. If you use custom dictionaries, you should update their entries.
  • The :item_path variable has been renamed as :i18n_key: you should search and replace it.

Renamed nav helper methods and files

The javascript-powered helpers (pagy*_responsive_nav, pagy*_compact_nav and items_selector) have been renamed in order to be simpler and more descriptive of their arguments, features and requirements. They have been improved internally, but you need only to search and replace because their usage has not been changed.

Bootstrap Extra

v2.0+ v3.0+
pagy_bootstrap_responsive_nav :breakpoints pagy_bootstrap_nav_js :steps
pagy_bootstrap_compact_nav pagy_bootstrap_combo_nav_js

Bulma Extra

v2.0+ v3.0+
pagy_bulma_responsive_nav :breakpoints pagy_bulma_nav_js :steps
pagy_bulma_compact_nav pagy_bulma_combo_nav_js

Foundation Extra

v2.0+ v3.0+
pagy_foundation_responsive_nav :breakpoints pagy_foundation_nav_js :steps
pagy_foundation_compact_nav pagy_foundation_combo_nav_js

Materialize Extra

v2.0+ v3.0+
pagy_materialize_responsive_nav :breakpoints pagy_materialize_nav_js :steps
pagy_materialize_compact_nav pagy_materialize_combo_nav_js

Plain > Navs Extra

v2.0+ v3.0+
pagy_plain_nav (pagy_nav alias) - removed -
pagy_plain_responsive_nav :breakpoints pagy_nav_js :steps
pagy_plain_compact_nav pagy_combo_nav_js
require "pagy/extras/plain" require "pagy/extras/navs"

Semantic Extra

v2.0+ v3.0+
pagy_semantic_responsive_nav :breakpoints pagy_semantic_nav_js :steps
pagy_semantic_compact_nav pagy_semantic_combo_nav_js

Renamed Pagy::VARS

Pagy::Vars[:breakpoints] has been renamed as Pagy::VARS[:steps]

Renamed items_selector

items_selector has been renamed as items_selector_js. It is also possible to use the i18n_key variable to customize the item name.

Renamed classes and params in helpers

Notice: You can skip the following sections if you did not override any nav helper.

CSS nav classes

The nav CSS classes follow the same renaming rules of the helper methods:

v2.0+ v3.0+
pagy-bootstrap-responsive-nav pagy-bootstrap-nav-js
pagy-bootstrap-compact-nav pagy-bootstrap-combo-nav-js
pagy-bulma-responsive-nav pagy-bulma-nav-js
pagy-bulma-compact-nav pagy-bulma-combo-nav-js
pagy-foundation-responsive-nav pagy-foundation-nav-js
pagy-foundation-compact-nav pagy-foundation-combo-nav-js
pagy-materialize-responsive-nav pagy-materialize-nav-js
pagy-materialize-compact-nav pagy-materialize-combo-nav-js
pagy-plain-responsive-nav pagy-nav-js
pagy-plain-compact-nav pagy-combo-nav-js
pagy-semantic-responsive-nav pagy-semantic-nav-js
pagy-semantic-compact-nav pagy-semantic-combo-nav-js

CSS JSON tag classes

Renamed first param passed to the pagy_json_tag:

v2.0+ v3.0+
:responsive :nav
:compact :combo_nav
:items :items_selector

Javascript functions

v2.0+ v3.0+
Pagy.responsive Pagy.nav
Pagy.compact Pagy.combo_nav
Pagy.items Pagy.items_selector

Dropped marginal methods

Dropped pagy_serialized, pagy_apply_init_tag and PagyInit javascript namespace. They were too basic to be useful as support for javascript powered custom component.

Commits

  • df3ebf2: updated performance assets for v3
  • 260e7d4: refactoring of i18n:
    • I18n gem compliant (removed plural keys inconsistent with the locale)
    • added tests for all the pluralization procs
    • simplified and normalized Pagy dictionary file
    • renamed :items_Path to :collection_key
    • added :collection_key lookup for the items_selector_js helper
  • 8ebb1ab: refactoring of test coverage:
    • replaced SingleCov with SimpleCov for CodeCov integration
    • simplified Travis setup
    • separate controls of RuboCop, SimpleCov, Oj and CodeCov with dedicated ENV variables
    • completed coverage at 100%
  • ae3e4bc: removed overridden method and undefined instance variable warnings
  • aa1aad9: renamed multi_series > sequels and relative js arguments and variables; renamed :sizes > :steps; updated nav test cases
  • 901f779: compact > combo and other changes
  • f7425a5: renamed assets according to helper names; reordered and improved nav docs
  • ad100eb: renamed plain extra to navs; removed the "plain_" qualifier for the helpers
  • 54f4a7e: updated docs and comments
  • 25470dd: renamed items_selector > items_selector_js
  • d4be9dd: replaced responsive with nav_js; reordered methods and test normalization
  • ec7d754: removed warning for locales zh-cn and zh-hk, and properly renamed as zh-CN and zh-HK
  • 2548557: simplified support extra (dropped pagy_serialized and pagy_apply_init_tag)
  • f82b2e7: fix typo
  • 704a4df: add french translation (#154)
  • 2f67dfa: UPDATE pagy locales id (#151)
  • 15732da: fix for broken migration link (#150)

Version 2.1.5

Changes

  • Fix for pagy_url_for returning duplicated params
  • Added "zh-TW" locale
  • Deprecated incorrect zh-cn and zh-hk locales in favor of zh-CN and zh-HK locales

Commits

  • 62ee172: fixes for pagy_url_for full url gets duplicated page param (#149)
    • used concatenated request.
      base_url instead of request.url
    • added better tests
    • fixed overriding of pagy_url_for in items extra
  • 0bca4af: fix for rubocop offense
  • 6b7fe77: deprecated zh-cn and zh-hk locales, normalized as zh-CN and zh-HK
  • c28d21a: Added locale zh-TW (#147)
  • 95cdf61: docs improvements
  • 310c804: Update lib/locales/README.md (#148)

Version 2.1.4

Changes

  • Fix for responsive javascript not working with IE

Commits

  • 3cce19a: javascript responsive refactoring:
    • replaced problematic loop functions with plain for loops
    • better naming in responsive function
    • shortened rendering time
    • simpler doc
  • 3eacc8d: better doc examples
  • 5c92187: added missing event listeners to auto-incremental javascript example
  • 119ba78: fix for support.md auto-incremental javascript example
  • 1f376d4: Update support.md (#146) Fix incremental/infinite scroll examples

Version 2.1.3

Changes

  • added Swedish locale

Commits

  • f0dcac6: docs update
  • 9dc8c05: removed tap from Pagy#series
  • e237ea6: add translation for Swedish (#145)
  • 7d71699: lighter README and a few docs improvements
  • f25b5f5: updated support extra examples with pagy_countess
  • 6fa1d03: comments and docs updates
  • 9f057e6: small improvement for the headers extra doc
  • 2c53e76: a few docs changes for the migration guide
  • 2a90e6a: stop following the GitFlow conventions (extra complexity for no advantage for this project; too many merge commits; history difficult to follow)
  • 198883c: Fix legacy changelog url (#144)

Version 2.1.2

Changes

  • Added "Current-Page" header to headers extra

Commits

  • 016b377: added Current-Page default header to headers extra
  • 4086181: fix compilant > compliant typos in docs
  • 5d6edf6: reconciliation between test strings

Version 2.1.1

Changes

  • Fix for multiple pagy get wrong url (#143)
  • Better documentation for responsive breakpoints (#140)

Commits

  • e9f467a: fix for pagy_url_for not working with multiple pagy in the same request (#143)
  • 75329b4: better configuration example and documentation for responsive breakpoints (#140)

Version 2.1.0

Changes

  • Added headers extra
  • Added support for overflow :last_page to searchkick and elasticsearch_rails extras
  • Added zh-hk locale
  • Better docs

Commits

  • 4e83993: updated docs
  • 8bd19d6: added headers extra (#141)
  • 9a26b04: added support for overflow :last_page to searchkick and elasticsearch_rails extras (#138)
  • c5524fc: Add zh-hk language yml file (#139)
  • 6d5841b: update travis setup
  • 5bd7c0a: updated docs notes for the searchkick and elasticsearch_rails extras
  • 0ca5539: updated comments in config/pagy.rb (#138)

Version 2.0.1

Changes

  • Fix for missing require in config/pagy.rb and fix for pt-br translation typos
  • docs fixes and improvements

Commits

  • 4e7b30f: update Repository Info section in README
  • 835e8d2: Fix for missing require in config/pagy.rb and fix for pt-br translation typos (#134)
  • 8cdffa1: updated consumption chart in README
  • 91aa417: replaced efficiency table with resource-consumption chart; other minor fixes
  • 964ab24: fix for wrong params in searchkick synopsis example
  • 71baf56: added paginate-responder link to the HTTP header in the How to doc (#132)
  • 05e218c: fix for indents in searchkick doc
  • 7061086: added README comments for the docs/update branch
  • b93259c: fix typos in searchkick readme (#129)
  • 4156121: added how-to for custom count for custom scopes (#130); other minor adjustments
  • 4c7b50a: docs fixes and additions (#128)

Version 2.0.0

Breaking Changes

Renamed methods and files (already deprecated)

The following methods have been renamed. You only need to search and replace, because the functionality has not been changed.

Bootstrap Extra

Legacy Name New Name
pagy_nav_bootstrap pagy_bootstrap_nav
pagy_nav_compact_bootstrap pagy_bootstrap_compact_nav
pagy_nav_responsive_bootstrap pagy_bootstrap_responsive_nav

Bulma Extra

Legacy Name New Name
pagy_nav_bulma pagy_bulma_nav
pagy_nav_compact_bulma pagy_bulma_compact_nav
pagy_nav_responsive_bulma pagy_bulma_responsive_nav

Foundation Extra

Legacy Name New Name
pagy_nav_foundation pagy_foundation_nav
pagy_nav_compact_foundation pagy_foundation_compact_nav
pagy_nav_responsive_foundation pagy_foundation_responsive_nav

Materialize Extra

Legacy Name New Name
pagy_nav_materialize pagy_materialize_nav
pagy_nav_compact_materialize pagy_materialize_compact_nav
pagy_nav_responsive_materialize pagy_materialize_responsive_nav
Legacy Name New Name
pagy_plain_nav (pagy_nav alias)
pagy_nav_compact pagy_plain_compact_nav
pagy_nav_responsive pagy_plain_responsive_nav
require "pagy/extras/navs" require "pagy/extras/plain"

Semantic Extra

Legacy Name New Name
pagy_nav_semantic pagy_semantic_nav
pagy_nav_compact_semantic pagy_semantic_compact_nav
pagy_nav_responsive_semantic pagy_semantic_responsive_nav

Renamed classes and params in helpers

Notice: You can skip this section if you did not override any nav helper.

CSS nav classes

The nav CSS classes follow the same renaming rules of the helper methods. For example:

pagy-nav-responsive-bootstrap is now pagy-bootstrap-responsive-nav

CSS JSON tag classes

All the JSON tag classes are now pagy-json, and the qualifier of the json (e.g. compact, responsive, ...) is now passed as the first argument in the array content. For example:

<script type="application/json" class="pagy-responsive-json">["#{id}", ...

is now

<script type="application/json" class="pagy-json">["responsive", "#{id}", ...

However the tags have been refactored to use the shared pagy_json_tag helper. For example:

script = pagy_json_tag(:responsive, id, tags, responsive[:widths], responsive[:series])

Pagy I18n

  • The Pagy::Frontend::I18N is now Pagy::I18n
  • The Pagy::I18n.load method accepts params in different format: see the doc

Searchkick Extra

  • The searchkick extra has been refactored and its methods work in a different way: see the doc

Elasticsearch Rails Extra

  • The elasticsearch_rails extra has been refactored and its methods work in a different way: see the doc

Pagy Initializer

The pagy initializer has changed: please replace/update it: pagy.rb

Commits

  • a03ff0b: updated README and docs
  • 7bb34e3: refactoring of searchkick extra
  • 22afe14: refactoring of elasticsearch_rails extra
  • fc608e9: setup for ruby 1.9+ and jruby 1.7+
  • 7948394: updated magic comments
  • d8ce15f: updated Gemfile
  • 8ad362d: conditional SingleCov
  • 14a7c30: small change in pagy_bootstrap_compact_nav style
  • 8550b67: removed fork from tests
  • 532743d: simpler overflow module structure (backward compatible)
  • 9c1212b: i18n refactoring:
    • added multi-language
    • simpler I18N format
    • faster pagy_t method
  • 1f3c86f: fixed warnings and a few backward incompatible statements
  • a6a9371: replaced +'...' syntax with EMPTY + '...' syntax for backward compatibility
  • 32984a3: removed empty string assignation (simpler and more efficient)
  • e61b1e6: Removed deprecations:
    • remove deprecation of method extras and extra CSS class in navs in modules
    • remove deprecated method from extras/shared
    • remove extra CSS class in tests and templates
    • remove the extras/navs.rb and DEPRECATIONS.md files
    • remove support for overridden legacy helper in pagy.js
    • add a note in the GoRails section in the README
  • ca18374: Cache gemset for faster Travis builds (#126)
  • 71a3e5f: Always return the same object as Pagy.root (#125)* Update copyright notice to 2019 (#121)

Version 1.3.3

Commits

  • 11944e4: Fix typo in class name (#122)
  • 40ef36d: travis badge links to branches
  • 78141a3: Improve default German translation (#120)
  • c0fe0df: minor typo corrections (#119)

Version 1.3.2

Changes

  • fixed responsive javascript error

Commits

  • 332c6a2: fix for responsive javascript error in slow loading pages (#115)

Version 1.3.1

Changes

  • fixed issue with items extra rendered after pagy_nav

Commits

  • 3ead159: fix for temp items variable in items extra (#117)

Version 1.3.0

Changes

  • Added Dutch locale
  • Refactoring of plurals
  • Javascript refactoring: fixes and improvements

Commits

  • 70796d3: Add Dutch language (#116)Added Dutch dictionary
  • b112f30: plurals.rb:
    • added frozen variables
    • added explicit 'zero' for :zero_one_few_may_other
    • added :pl pluralization (#111)
    • added README.md
  • 6cf86af: added docs notes
  • b05db4e: added optional target element to Pagy.init for AJAX support
  • e35a9ae: refactoring of default element ids for helpers and deprecation:
    • default ids are now constant also in different processes and compliant also with older HTML versions
    • simpler deprecation code
  • cef823c: pagy.js responsive refactoring (#115):
    • moved handling of window event listeners into the responsive function
    • added resize delay to Pagy.responsive
  • a64a8bd: fix for items test
  • e9ab4a1: renamed argument items > fetched; removed redundant assignation

Version 1.2.1

Changes

  • Improved efficiency for count in backend and extras

Commits

  • 01178cb: replaced enumerable #count for #size (suggested in #112)
  • 0723228: count (and page) in *get_variables methods are set only when nil (alternative implementation of #112)

Changes

  • Deprecation of frontend helpers (see Deprecations)
  • Internal refactoring of javascript
  • added support extra for features like: incremental, infinite, auto-scroll pagination

Commits

  • 2bdb1e5: added support extra
  • 055af34: added :cycle variable to Pagy
  • c1680ec: add JSON/javascript deprecations
  • 263c12d: better internal javascript naming
  • 910facc: updated tests
  • 6e8cde8: refactoring of json tags:
    • single "pagy-json" class for all json tags
    • removed prefix used for all pagy element ids
    • extracted pagy_json_tag from frontend extras
  • 98ce8e7: automatic ids based on hash must be strings
  • cd8256e: added DEPRECATIONS file
  • 6e3e1e4: deprecated and renamed navs helpers in code, tests and docs; renamed navs extension as plain; added :pagy_plain_nav alias of :pagy_nav
  • 4bc72af: deprecated and renamed semantic navs in code, tests and docs
  • a9c3822: deprecated and renamed materialize navs in code, tests and docs
  • 5481f40: renamed foundation templates and updated docs
  • f984f83: deprecated and renamed foundation navs in code, tests and docs
  • 95045c3: renamed bulma templates and updated docs
  • dbad41a: deprecated and renamed bulma navs in code, tests and docs
  • e4f859f: renamed bootstrap templates and updated docs
  • 35078ab: deprecated and renamed bootstrap navs in code, tests and docs
  • 04ec860: added Pagy.deprecate method

Version 1.1.0

Changes

  • added Pagy::Countless support subclass
  • added countless extra integrated with the other extras

Commits

  • fa547ad: reworded the doc notice about :pagy_countless_get_items returning an Array; fix typo in comment
  • 8368a99: Synopsys > Synopsis
  • 1b8a080: a few fixes for the countless tests
  • 0bf2f46: Typo fix
  • 2f93227: Test coverage for Pagy::Countless (#108)
  • c2a25b6: Add some tests for Countless extra (#108)
  • 04fa7a2: added countless support to the items extra
  • f272faf: added countless support to the overflow extra
  • 7b25165: added countless sub-class and extra code
  • 37cf51a: series can return an empty array if size is empty
  • 7bf9d14: fix for a few missing lines in the extras docs

Version 1.0.0

Breaking Changes

  • The "Out Of Range" concept has been redefined as "Overflow" resulting in a few renaming. Since there are no changes in the logic, you can update by just searching and replacing (if present) a few string in your code:
    • "/out_of_range" > "/overflow"
    • "OutOfRangeError" > "OverflowError"
    • ":out_of_range_mode" > ":overflow"
    • "out_of_range? > "overflow?"

Commits

  • 431e4db: redefined the "Out Of Range" concept as "Overflow" (#103):
    • files: out_of_range* > overflow*
    • extra name: out_of_range > overflow
    • error class: OutOfRangeError > OverflowError
    • module: OutOfRange > Overflow
    • VARS: :out_of_range_mode > :overflow
    • instance variable: @out_of_range > @overflow
    • method: out_of_range? > overflow?
  • a0b411a: added README note about semantic versioning

Version 0.23.1

Commits

  • e8a2251: fix for ArgumentError feedback in Pagy constructor should show the originally passed offending value (#104)

Version 0.23.0

Changes

  • Added German locale

Commits

Version 0.22.0

Changes

  • Added Norwegian locale

Commits

  • 59064c2: Added Norwegian locale (#100)

Version 0.21.0

Changes

  • Added Elasticsearch Rails extra

Commits

  • e0acbf6: fix for robocop offenses (#96)
  • d082729: Added Elasticsearch Rails extra (#96)
  • dd0575a: fix doc typo
  • d3510a7: updated comments in plurals.rb
  • 5ed458a: added README disclaimer
  • 02b0a74: Update link to Semantic-UI pagination

Version 0.20.0

Breaking Changes

  • I18n refactoring
    • Combo helpers and item selectors have been refactored: you should update your overridden helpers accordingly
    • The i18n structure has changed, update your custom i18n locale file

Commits

  • bab3bf8: Merge pull request #93 from ddnexus/i18n
  • f8017c3: updated config/pagy.rb
  • c461885: added ru plural proc
  • 5680628: fix for wrong indentation in zh-cn
  • 6998932: pagy.yml properly split into separate locale files
  • e3048f2: improvements for ja tr and zh-cn
  • c1aec4c: removed marginal pagy.nav.current key used only by foundation an fixed consistency in foundation helpers and templates
  • 15e413b: refactoring of the composition of i18n strings for compact helpers and items selector

Version 0.19.4

Changes

  • added Japanese, Brazilian and Indonesian translations

Commits

  • 67f1bb9: reordered translations
  • 4ab3438: adding pt-br translation (#87)
  • d1f8674: alpha-ordered translations
  • 0311c85: Added Indonesian locale (#91)
  • ce68624: add japanese translation (#89)
  • 5e4f93d: corrected chinese translation (#88)
  • 26654d4: updated README

Version 0.19.3

Changes

  • added Chinese locale

Commits

Version 0.19.2

Changes

  • added Russian locale

Commits

  • 099731b: small improvement in i18n docs
  • ef77ae2: Added ru translation (#84)
  • d4b49d6: Fixed a typo in spanish locale. (#85)
  • a91b72a: small description change for items selector UI
  • a62b222: small refactoring of the trim extra
  • c23505a: fix for doc typo

Version 0.19.1

Changes

  • added Spanish locale

Commits

Version 0.19.0

Important Changes

  • added Semantic extra

Commits

  • b65a905: fix for page_link string and conflicts, added compact and responsive navs tests and docs (#73)
  • f147cca: Semantic UI nav helper (#73)
  • 429ac14: updated pagy.js requirements info

Version 0.18.0

Breaking Changes

  • The extras dir has been reorganized:
    • the helpers in the compact and responsive extras have been integrated into the respective frontend extras (bootstrap, bulma, foundation, materialize and navs), so the compact and responsive extras are gone. You should remove them from the pagy.rb initializer, eventually adding the navs extra if you use its plain helpers.
    • The templates and javascripts dirs have been moved to lib. If you use rails and the pagy.js you should update the assets.paths in the pagy.rb initializer.

Commits

Version 0.17.0

Important Changes

  • added Foundation extra

Commits

  • ba7d046: added fixes and improvements for foundation extra:
  • d78f85d: Foundation extra (#79)
  • 13f37a0: restyling of extras in README, docs and initializer example

Version 0.16.0

Important Changes

  • added Searchkick extra

Commits

  • 6143f5f: docs fixes
  • 5227bd1: added searchkick references in docs, README and initializer example (#75)
  • b9acf0c: Searchkick extra (#75)

Version 0.15.1

Important Changes

  • small bugs and compatibility fixes

Commits

  • 1436833: added Post and Tutorials links and Credits section
  • 4e400c6: items are adjusted only for non-empty pages
    (avoid sequel error for limit 0)
  • 3abcc11: fix for sinatra: page_param must be converted to string
  • ad8c311: fix for trim extra missing when current page=1
  • 6f774d7: added missing doc menu for trim extra
  • 5f6de56: added a few tests for the trim extra

Version 0.15.0

Important Changes

  • Added Trim extra

Commits

  • 868be25: added trim extra
  • fb48976: chained method for extra items overrides

Version 0.14.0

Important Changes

  • Added Materialize extra

Commits

  • b0e959b: added stars github button to docs
  • 1dd38c9: added materialize extra (#44)
  • 30c1a57: updated docs

Version 0.13.1

Important Changes

  • Improvements and fixes for the out_of_range extra

Commits

  • 419af23: better comments and error message for unknown out_of_range_mode variable
  • 48fa056: use case (#71)
  • 36761a8: fix :last_page mode in out_of_range extra (#70)
  • 5958dc5: out_of_range extra improvements (#68)
  • 7177cd9: added note about the out_of_range exta to the README

Version 0.13.0

Important Changes

  • Added Out Of Range extra

Commits

  • c52db2a: added out_of_range extra (#68)
  • 37119ee: fixes for bulma extra in initializer_example.rb and docs
  • 05729f6: removed redundant condition in Pagy initialize
  • 2e75247: small fixes in CHANGELOG
  • 3d732dd: docs for empty page OutOfRangeError (#69)
  • a3be52e: Fixed screenshot name for compact bulma extra (#67)

Version 0.12.0

Important Changes

  • Added Bulma extra
  • Added Turkish language in pagy dictionary
  • Improved consistence and compatibility in extras

Commits

  • 8fc91df: fix for "area" typos in bulma extra (#62) and consistency improvement across responsive helpers
  • bb0060f: fix for window event listeners persisted by turbolinks (#64)
  • d759f91: Bulma extra (#62)
  • 9f09837: Fixed typo in some extras usage (#63)
  • 2ce572a: Added Turkish locales (#58)
  • dd361e1: replaced Array.from in pagy.js for extended compatibility
  • bb1243d: fix typos
  • da34b94: fix typo (#61)
  • cdab354: added better I18n comments in initializer_example.rb

Version 0.11.2

Commits

  • 76fc273: used merge! in place of **

Version 0.11.1

Important Changes

  • All tests has been converted to the spec syntax

Commits

  • b14f446: converted tests to spec syntax
  • 0422568: cleanup of i81n plurals in code and docs
  • 5a2db16: added a few missing references to docs
  • 6028980: enabled rubocop Naming/UncommunicativeMethodParamName
  • 1de470b: small improvements and fixes for docs
  • 228109e: fix for typos
  • 0a1ed94: updated initializer_example.rb

Version 0.11.0

Breaking Changes

  • The pagy_nav_bootstrap_compact and pagy_nav_bootstrap_responsive helpers have been renamed as pagy_nav_compact_bootstrap and pagy_nav_responsive_bootstrap to keep the consistency with the extras structure (and support the consistency of future framework additions). Please, rename them accordingly.
  • The Pagy::Frontend::I18N.load_file has been renamed as Pagy::Frontend::I18N.load and expects a hash as the argument. See the I18n doc.
  • The seldom used Pagy::Frontend::I18N[plurals] has been renamed as Pagy::Frontend::I18N[plural]. See the I18n doc.
  • Please, update your initializer file to the new initializer_example.rb

Commits

  • 826798b: refactoring of I18N constant: added support for multiple static languages and plural rules
  • b78b71e: better naming of json classes
  • ea7c22b: breaking change for compact and responsive extras: renaming of pagy_nav_bootstrap_* helpers to pagy_nav_*_bootstrap, consistent with extras structure
  • 7d1a573: internal consistency renaming of local/test variables; minor fixes and improvements

Version 0.10.1

Commits

  • 8ca5013: doc improvements
  • 99081ee: small performance improvement for #51 and updated doc
  • 5863ceb: add support for counting grouped collections (#51)

Version 0.10.0

Breaking Changes

  • The javascript used by the compact, items and responsive extras has been refactored in order to avoid any usafe-inline vulnerability. The javascript files have been replaced by one shared file, and require to set an event listener or run a function on window load. See the commit below and the javascript doc for details.

Commits

  • 50e304b: javascript refactoring in order to avoid any usafe-inline vulnerability (#52):
    • removed inline scripts from all extras
    • one single pagy.js file shared among all the extras
    • the Pagy.init function should be executed at document load
    • updated tests and docs
  • 32d0afc: doc fix: UI selector > selector UI

Version 0.9.2

Important Changes

  • 100% test coverage for core code and extras

Commits

  • c11dfcc: updated README
  • f8728e7: added responsive extra tests
  • 14ea356: added compact extra tests
  • 2ac743c: added bootstrap extra test
  • 159b65e: added i18n extra tests
  • 4449c95: normalization of indentation and empty lines
  • 9afa9d9: Default Frozen Strings for Ruby Files (#49)
  • 036c87f: Fix typo in :page_param configuration (#48)

Version 0.9.1

Important Changes

  • Improved the Pagy::OutOfRangeError exception, now storing the failed Pagy object

Commits

  • 9817659: improved OutOfRangeError exception (storing the failed Pagy object)
  • 7195229: a few minor consistency fixes for the initializer_example.rb
  • 00cd5bb: Fix typo (#45)
  • 8b3a733: fix for header level in README

Version 0.9.0

Important Changes

  • Improvements for compact extra
  • Refactoring of test structure and tasks
  • Added items extra: Allow the client to request a custom number of items per page with an optional selector UI

Commits

  • 45dd700: added array extra tests
  • 623ce9e: Added items extra and tests
  • 5902e8a: passed instance variables are not deleted from the vars hash
  • e2efb89: refactoring of test structure
  • 1cc335b: documentation updates
  • 42ba053: improved pagy-compact.js
  • 8c20fa2: Enable support for return key inside Pagy compact input (#43)
  • d4b751c: Documentation updates
  • 9f0dec9: fix for broken links in docs
  • c41e823: additions and normalization of documentation and comments
  • 38f9a49: fix for broken links in readme

Version 0.8.6

Commits

  • 0db9125: minor documentation improvements
  • 8a0a5a8: broader parallel assignment in methods (improved performance)
  • 18baa8b: small optimization (used a few less objects)
  • 1d16e3d: documentation additions
  • 62c420d: updated doc and charts

Version 0.8.5

Important Changes

  • Fix for I18n exception on startup

Commits

  • 31b815d: fix for I18n exception on startup (#41)

Version 0.8.4

Important Changes

  • Improved readability and memory allocation
  • All the core methods are tested
  • Better documentation

Commits

  • c25493a: added backend tests
  • 48dbe17: docs small fixes and improvements
  • 5e9b7d4: add Pagy::VARS[:item_path] empty default
  • ce8d09a: add Pagy::VARS[:anchor] empty default
  • 8789edd: improve readability of pagy_link_proc (#40):
    • remove conditions to avoid extra spaces inside the html tags
    • add Pagy::VARS[:link_extra] empty default
  • 2f0d384: docs additions

Version 0.8.3

Important Changes

  • Refactoring of I18N to fix #39

Breaking Changes

  • Pagy::I18N has been moved to Pagy::Frontend::I18N: you should update the initializer in case you set any of the Pagy::I18N variable.

Commits

  • d85791e: Refactoring I18N to fix #39:
    • moved I18N from Pagy to Pagy::Frontend - I18N_DATA moved to Pagy::Frontend::I18N[:data]
    • I18N[:file] removed
    • added I18N.load_file method
    • updated initializer and doc
  • 504b943: added missing tests to frontend_test.rb
  • 6168df3: use actionable code coverage (#29)

Version 0.8.2

Important Changes

  • Fix for PostgreSQL: collection.count(:all)

Commits

  • 8a204dc: fix for count in Pagy::Backend (#36)
  • 29ed221: added test_check_variable_defaults
  • 1263540: indentation fixes for tests
  • a927d57: improve testability by sorting tests into files that mirror lib + group by method (#37)
  • 5a5178b: docs markdown normalization and README editing
  • 11fbc49: examples was using responsive instead of i18n (#35)
  • b9517d3: docs small fixes and improvements
  • 8bc6900: fix for typos and omissions in initializer_example.rb
  • a09558e: avoid uncoverable code by not using gemspec in Gemfile (#33)
  • a585fe6: clean up gemspec (#32)
  • ae48f13: test on all supported ruby versions and split out rubocop (#30)

Version 0.8.1

Important Changes

  • Added :params and :anchor variables to control the generation of arbitrary URLs per pagy instance

Commits

  • d185461: added :params and :anchor variables to control the generation of arbitrary URLs per pagy instance
  • 62fd6c8: better forward improvements with pagy_url_for receiving the pagy instance instead of the single pagy_param
  • 748de0d: readme and changelog improvement

Version 0.8.0

Important Changes

  • Added an initializer_example.rb that you can use as an easy template for Pagy configuration
  • Integrated extras from pagy-extras gem into Pagy, now optionally required in the initializer
  • Refactored I18n: simpler code and simpler configuration now based on the I18n extra
  • Reorganized documentation and added a few more how-to topics
  • Added the :page_param variable used to get the page number from and create the URL for the page links

Breaking Changes

  • Extra are now integrated in Pagy. The pagy-extras gem has been discontinued: you should remove it and update your code as indicated here
  • Pagy I18n has been refactored and it's simpler to use. The main change is that the Pagy::I18N[:gem] variable has been removed, so if you want to use the I18n gem in place of the internal Pagy implementation you need just to require 'pagy/extras/i18n' in your initializer. (see the I18n doc)
  • pagy_url_for now requires the pagy argument and it's called with 2 params: if you have overridden it, you should add the extra param.
  • pagy_get_vars now requires the vars argument and it's called with 2 params: if you have overridden it, you should add the extra param.
  • pagy_array_get_vars now requires the vars argument and it's called with 2 params: if you have overridden it, you should add the extra param.

Commits

  • 3e2ad90: added CHANGELOG.md with the latest few versions (#23)
  • f42811d: implemented :page_param (#20)
  • 9040593: added doc about skipping single pages navs
  • 605169a: added initializer_example.rb
  • daa2c1b: I18n refactoring:
    • simplified the pagy_t definition
    • simplified the configuration of I18n now using the I18n extra to override the pagy_t
    • modules are now required and not autoloaded
    • updated doc
  • 9b0d420: style cleanup (#24)
  • dbc5a05: Add prev/next links to compact Bootstrap nav and improve HTML semantics (#21)
  • f764cc1: refactoring extras:
    • moved pagy-extra files into the extras dir - moved the templates dir into the extras dir
    • updated the documentation
  • befe92d: Fixes Travis Badge (#25)
  • 3862d26: Travis (#22)
  • 7050ee0: basic rubocop setup (#18)
  • 7aee689: small docs improvements

Version 0.7.2

Commits

  • 527cd8e: simplification of pagy_url_for
  • 144c997: URIs are always joined with forward slash (#11)

Version 0.7.1

Commits

  • 92260b5: docs and comments refactoring, also updated for pagy-extras v0.2.0
  • 46f235e: Remove a couple of Range allocations (#10)
  • 39c5470: Only use and/or keywords for control flow (#12)
  • 26bb29c: fix for last page items adjusted to 0 (#3)
  • 860c579: small improvement: to_i called only once in core variables set
  • b18f254: Merge pull request #5 from artinboghosian/patch-1
  • 03d6ee8: Update README.md
  • 358a806: a couple of links added to the doc
  • fac1f34: docs small fixes and improvements

Version 0.7.0

Commits

  • e480d04: better handling of blank core variables
  • b56ef9e: docs fixes and improvements
  • 987be15: I18N[:plurals] proc uses symbols and returns a frozen string
  • 6ea7a48: changes for PR #2: - use of local variable zero_one instead of constant I18N_PLURALS - VARS cannot be frozen
  • fb8975b: Freeze class immutable constant variable at first

Version 0.6.0

Commits

  • ec8e268: created pagy-extras gem:
    • moved pagy_nav_bootstrap method and templates
    • updated docs files (fixes and use of relative links)
  • facc4c3: unescaped notation for all erb usage
  • 26c068f: moved require yaml in Frontend
  • f5782d8: added css classes to pagy helper navs
  • cee02c6: helper tags are strings
  • 4788c3c: grouped the variables :initial, :before, :after and :final into one single :size array; #series can work with different sizes
  • e610b82: minor docs fixes

Retype.Yml (`docs/retype.yml`)

start:
pro: true
open: false

input: .
output: .public
url: https://ddnexus.github.io/pagy/

include:

  • "/assets/nav.html.erb"
  • "/gem/apps/enable_rails_page_segment.rb"
  • "/gem/config/pagy.rb"
  • "/gem/locales/en.yml"
  • "/gem/stylesheets/pagy.css"
  • "/gem/stylesheets/pagy-tailwind.css"

exclude:

  • "CLAUDE*.md"

branding:
title: Pagy
logo: assets/images/pagy-the-frog.png
label: 43.8.1
colors:
label:
text: "#ffffff"
background: "#1f7a1f"

meta:
title: " 💚 Pagy"

links:

search:
mode: full

backlinks:
enabled: false

breadcrumb:
home: Pagy
separator: ":icon-arrow-right:"

theme:
base:
base-color: "#008000"
dark:
base-color: "#7cb342"

lastUpdated:
date:
enabled: true
source: committer

nav:
mode: stack
icons:
mode: pages

nextprev:
mode: exclude

footer:
copyright: "© 2017-{{ date.now.year }} • Domizio Demichelis • MIT License"


Guides Choose Right (`docs/guides/choose-right.md`)


label: Choose Right icon: thumbsup order: 95

:icon-thumbsup:  Choose the right tool


Pagination just means retrieving a (potentially big) collection of items in small sequential chunks (pages).

Pagy offers four different pagination techniques, each implementing different types of paginators. Choose the right one to ensure the best performance and workflow.

Pagination Types

:icon-move-to-end:  OFFSET {#offset}

The most common pagination technique is counting the collection items (COUNT DB query), using the count to calculate where the specific pages start (i.e., OFFSET) and retrieving only the LIMIT items for that page (OFFSET+LIMIT DB query).

It is straightforward to understand and set up and very versatile for the UI, but it also has a few shortcomings.

==- Shortcomings

  • DB Performance
    • Counting records might be quite expensive in terms of execution time and DB load. The standard OFFSET pagination causes the DB to count twice per page: once for the total records and another for the records to skip.
  • Data Shift
    • If records are created or deleted during browsing, the calculated OFFSET may become inconsistent. The user may see previous records again or miss records entirely.
    • Notice that querying the precise count for each page DOES NOT fix the data-shift problem.

!!!tip For better DB performance with OFFSET...

==- Paginators  [!badge variant="contrast" size="xs" corners="pill" text="3"] {#offset-paginators}

{.list-icon}

  • :icon-move-to-end: :offset
    • :icon-check-circle: Best for: Standard App (Small Data)
    • :icon-thumbsup: Pros: Simple setup, full UI support
    • :icon-thumbsdown: Cons: Slow on big tables (two queries per page), data-shift

{.list-icon}

  • :icon-move-to-end: :countish
    • :icon-check-circle: Best for: Standard App (Large Data)
    • :icon-thumbsup: Pros: Same as :offset, but far better performance than :offset (memoizes the total count)
    • :icon-thumbsdown: Cons: Same as :offset, the count may become stale (likely a negligible side effect)

{.list-icon}

  • :icon-move-to-end: :countless
    • :icon-check-circle: Best for: API, Infinite Scroll
    • :icon-thumbsup: Pros: Fastest in the offset family (only one query per page)
    • :icon-thumbsdown: Cons: Same as :offset, the count is always nil, UI support with a few limitations

===

:icon-key:  KEYSET {#keyset}

The KEYSET pagination technique allows the fastest and lightest DB performance. It does not count the (ordered) collection (which makes it faster), nor does it calculate any numeric page pointers in advance (which avoids the data-shift during browsing). It just uses the values in the last record of the page to retrieve the next page.

==- Shortcomings

  • It knows only the current page and the pointer to the next page.
  • Page pointers are encoded strings and the count is not known.
  • It supports only APIs and infinite scrolling.

!!!tip For UI support with KEYSET pagination...
Use the :keynav_js paginator.
!!!

==- Paginators  [!badge variant="contrast" size="xs" corners="pill" text="2"] {#keyset-paginators}

{.list-icon}

  • :icon-key: :keyset
    • :icon-check-circle: Best for: API, Infinite Scroll
    • :icon-thumbsup: Pros: Fastest paginator, no data-shift, fastest single query per page
    • :icon-thumbsdown: Cons: Very limited UI support, appropriate DB indices required

{.list-icon}

  • :icon-key: :keynav_js
    • :icon-check-circle: Best for: Standard App (Large Data)
    • :icon-thumbsup: Pros: All the pros of :keyset+:countless, numeric pages
    • :icon-thumbsdown: Cons: Same as :countless, requires JavaScript Support (or it falls back to the :countless paginator)

===

:icon-calendar:  TIME-RANGE {#time-range}

This hybrid technique filters by a specific time period (Year, Month, Day, etc.) and applies the offset paginator within that period.

==- Paginator  [!badge variant="contrast" size="xs" corners="pill" text="1"]

{.list-icon}

  • :icon-calendar: :calendar
    • :icon-check-circle: Best for: Time-series, log collections
    • :icon-thumbsup: Pros: Natural navigation for date-based data
    • :icon-thumbsdown: Cons: The setup for the UI is more involved

===

:icon-search:  SEARCH {#search}

Pagy supports ElasticsearchRails, Meilisearch, ActiveSearch, Searchkick, and TypesenseRails.

The search paginators get the count, limit and results provided by the search platform. Pagy acts as an interface to these underlying gems, using the OFFSET technique, without its shortcomings.

==- Paginators  [!badge variant="contrast" size="xs" corners="pill" text="5"]

{.list-icon}

===


Guides How To (`docs/guides/how-to.md`)


label: How To order: 90 icon: tools

:icon-tools:  How To


This page provides practical tips and examples to help you use Pagy efficiently.

==- Choose the right pagination technique

Check the Choose Right Guide

==- Control the items per page

Fixed
: Set the :limit option to the number of items to serve with each page.

Requestable
: Set the :client_limit option to allow the client to request up to the :client_limit. If the client omits to request a specific limit, pagy will fall back to its regular behavior. See also Customize the URL keys below.

Interactive
: Use the limit_tag_js helper to provide a UI selector to the user.

==- Customize the URL keys

  • Set page_key: 'custom_page' to customize URL generation, e.g., ?custom_page=3.
  • Set the :limit_key to customize the limit param the same way. (Only useful if :client_limit is allowed).

See URL Options

==- Control the pagination bar

You can customize the number and position of page links of the series_nav and series_nav_js navigation bars using:

==- Force the :page

Pagy retrieves the page from the 'page' request params hash. To force a specific page number, pass it directly to the pagy method. For example:

ruby controller
@pagy, @records = pagy(:offset, collection, page: 3) # force page #3

==- Handle user input

See Safety

==- Customize the ARIA labels

You can customize the aria-label attributes of any *nav* helper by passing the :aria_label option to the helper.

You can also replace the pagy.aria_label.nav strings in the dictionary, as well as the pagy.aria_label.previous and the pagy.aria_label.next.

See ARIA.

==- Paginate with JSON:API nested URLs

Enable jsonapi: true, optionally providing :page_key and :limit_key:

ruby
# JSON:API nested query string: E.g.: ?page[number]=2&page[size]=100
@pagy, @records = pagy(:offset, collection, jsonapi: true, page_key: 'number', limit_key: 'size')

==- Customize the URL query

See the :querify Option

==- Add a URL fragment

See the URL Options

==- Add HTML attributes to the anchor tags (links)

Pass the :anchor_string option to the helper. It's especially useful for adding data-turbo-* or data-* Stimulus attributes.

==- Customize CSS styles

Pagy includes different formats of stylesheets for customization, as well as styled nav tags for :bootstrap and :bulma.

You can also override the specific helper method.

:::
==- Override the element "style" attribute

The input_nav_js and limit_tag_js use inline style attributes. You can override these rules in your stylesheet files using the [style] attribute selector and !important. Below is an example of overriding the width of an input element:

css
.pagy input[style] {
  width: 5rem !important; /* just a useless example */
}

:::

==- Override pagy methods

  • Identify the method file's path in the gem lib dir (e.g., 'pagy/...').
  • Note the name of the module where it is defined (e.g., Pagy::...).

Copy and paste the original method in the Pagy Initializer

ruby pagy.rb (initializer)
require 'pagy/...' # path to the overridden method file
module MyOverridingModule # wrap it with your arbitrarily named module
  def any_method # Edit or define your method with the identical name
    # Custom logic here...
    super
    # Custom logic here...
  end
end
# prepend your module to the overridden module
Pagy::AnyModule.prepend MyOverridingModule

If you need assistance, ask in the Q&A discussions.

==- Paginate an array

Simply pass it as the collection: pagy(:offset, my_array, **options)

==- Paginate ActiveRecord collections

Pagy works seamlessly with ActiveRecord collections, but certain collections may require specific handling:

:::

==- :icon-database:  Grouped collections

For better performance of grouped counts, you may want to use the :count_over option.

==- :icon-database:  Decorated collections

Do it in two steps:

Get the page of records without decoration

ruby controller
@pagy, records = pagy(:offset, Post.all)

Decorate the retrieved records

ruby controller
@decorated_records = records.decorate # or YourDecorator.method(records), whatever works

==- :icon-database:  Custom scope/count

If the default pagy doesn't get the right count:

ruby controller
# pass the right count to pagy (that will directly use it skipping its own `collection.count(:all)`)
@pagy, @records = pagy(:offset, custom_scope, count: custom_count) # Example implementation

==- :icon-database:  Ransack results

Ransack's result method returns an ActiveRecord collection that is ready for pagination:

ruby controller
q = Person.ransack(params[:q])
@pagy, @people = pagy(:offset, q.result)

==- :icon-database:  PostgreSQL collections

Always ensure your collections are ordered!

:::

==- Paginate for generic API clients

Explore the following options:

==- Paginate for JavaScript frameworks

You can send selected @pagy instance data to the client as JSON using the data_hash helper, including pagination metadata in your JSON response.

==- Paginate search platform results

See these paginators:

==- Paginate by date

Use the :calendar paginator for pagination filtering by calendar time units (e.g., year, quarter, month, week, day).

==- Paginate multiple independent collections

When you need to paginate multiple collections in a single request, you need to explicitly differentiate the pagination objects. Here are some common methods to achieve this:

:::
==- :icon-light-bulb:  Override the request path

By default, Pagy generates links using the same path as the request path. To generate links pointing to a different controller or path, explicitly pass the desired :path. For example:

rb controller
def index
  @pagy_foos, @foos = pagy(:offset, Foo.all, path: '/foos')
  @pagy_bars, @bars = pagy(:offset, Bar.all, path: '/bars')
end
erb
<%== @pagy_foos.series_nav %>
<%== @pagy_bars.series_nav %>

==- :icon-light-bulb:  Use separate Turbo Frame actions

If you're using hotwire (turbo-rails being the Rails implementation), another way of maintaining independent contexts is using separate Turbo Frame actions. Just wrap each independent context in a turbo_frame_tag and ensure a matching turbo_frame_tag is returned:

erb
<%# movies/index.html.erb %>

<%# movies#bad_movies %>
<%= turbo_frame_tag "bad_movies", src: bad_movies_path do %>
<%= render "movies_table", locals: {movies: @movies}%>
<%== @pagy.series_nav %>
<% end %>

<%# movies#good_movies %>
<%= turbo_frame_tag "good_movies", src: good_movies_path  do %>
<%= render "movies_table", locals: {movies: @movies}%>
<%== @pagy.series_nav %>
<% end %>
rb controller
def good
  @pagy, @movies = pagy(:offset, Movie.good, limit: 5)
end

def bad
  @pagy, @movies = pagy(:offset, Movie.bad, limit: 5)
end

==- :icon-light-bulb:  Use the root_key option

By default, pagy creates flat URLs for its links. If you need to handle multiple pagy instances in the same request, you can nest the :page and (if you use it) the :limit params by passing the :root_key option to the paginator:

rb controller
def index
  @pagy_stars, @stars     = pagy(:offset, Star.all, root_key: 'stars')
  @pagy_nebulae, @nebulae = pagy(:offset, Nebula.all, root_key: 'nebulae')
end

==- :icon-light-bulb:  Use different page keys

You can also paginate multiple models in the same request by simply using a different :page_key for each instance:

rb controller
def index
  @pagy_stars, @stars     = pagy(:offset, Star.all, page_key: 'pagy_stars')
  @pagy_nebulae, @nebulae = pagy(:offset, Nebula.all, page_key: 'pagy_nebulae')
end

:::

==- Paginate only MAX records

You may want to limit the availability of your records either for speeding up the DB queries (especially useful with OFFSET paginators with big tables), or simply to avoid exposing all your data to scrapers.

The best way to ensure it is to create a limited collection using an ActiveRecord Virtual Table:

rb
max_records     = 10_000
collection      = Product.where(...).limit(max_records)   # Add the max_records limit to your collection
limited         = collection.from(collection, :products)  # Create a limited collection using the :products Virtual Table
@pagy, @records = pagy(:offset, limited, **options)       # Paginate the limited collection

!!!success

  • It works with all OFFSET and KEYSET paginators.
  • It produces faster COUNT and OFFSET queries, limiting the table scan.
  • It enforces strict control over the quantity of records you expose.
    !!!

==- Paginate collections with metadata

When your collection is already paginated and contains count and pagination metadata, you don't need any pagy* controller method.

For example, this is a Tmdb API search result object, but you can apply the same principle to any other type of collection metadata:

rb
#<Tmdb::Result page=1, total_pages=23, total_results=446, results=[#<Tmdb::Movie ..>,#<Tmdb::Movie...>,...]...>

As you can see, it contains the pagination metadata that you can use to set up the pagination with pagy:

ruby controller
# get the paginated collection
tobj = Tmdb::Search.movie("Harry Potter", page: params[:page])
# use its count and page to initialize the @pagy object
@pagy = Pagy::Offset.new(count: tobj.total_results, page: tobj.page, request: Pagy::Request.new(request: request))
# set the paginated collection records
@movies = tobj.results

==- Skip single page navs

Unlike other gems, Pagy does not decide for you that the nav of a single page of results must not be rendered. You may want it rendered... or maybe you don't. If you don't:

erb
<%== @pagy.series_nav if @pagy.last > 1 %>

==- Maximize performance

==- Handle Pagy exceptions

:::

==- :icon-stop:  Pagy::OptionError

It is a subclass of ArgumentError that offers information to rescue invalid options. For example: with rescue Pagy::OptionError => e you can get access to a few readers:

  • e.pagy the pagy object
  • e.option the offending option symbol (e.g. :page)
  • e.value the value of the offending option (e.g. -3)

==- :icon-stop:  Pagy::RangeError

With the OFFSET pagination technique, users/clients may request a page beyond the end of the collection (e.g., when some records got deleted and a user follows a stale page link).

By default, Pagy doesn't raise any exceptions for requesting an out-of-range page. Instead, it does not retrieve any records and serves the navs as usual, so the user can visit a different page.

Sometimes you may want to take a different action, so you can set the option raise_range_error: true, rescue it and do whatever fits your app better. For example:

ruby controller
rescue_from Pagy::RangeError, with: :redirect_to_last_page

private

def redirect_to_last_page(exception)
  redirect_to url_for(page: exception.pagy.last), notice: "Page ##{params[:page]} is out-of-range. Showing page #{exception.pagy.last} instead."
end

:::

==- Test with Pagy

  • Pagy has 100% test coverage.
  • You only need to test pagy if you have overridden methods.

==- Use your own pagination templates

!!!warning Warning!
The pagy nav helpers are not only a lot faster than templates, but they also accept dynamic arguments and comply with ARIA and I18n standards.

Using your own templates is possible, but it's likely just reinventing a slower wheel.
!!!

If you really need to use your own templates, you absolutely can.

Here is a static example that doesn't use any other helper or dictionary file for the sake of simplicity; however, feel free to add your dynamic options and use any helper and dictionary entries as you need:

:::code source="/assets/nav.html.erb" :::

You can use it as usual: just remember to pass the :pagy local set to the @pagy object:

erb
<%== render file: 'nav.html.erb', locals: {pagy: @pagy} %>

!!!
You may want to look at the actual output interactively by running:

sh
pagy demo

...and point your browser to http://127.0.0.1:8000/template.
!!!

==- Use Pagy with non-rack apps

For non-rack environments that don't respond to the request method, you should pass the :request option to the paginator.

==- Use pagy outside controllers or views

The pagy method needs to set a few options that depend on the availability of the self.request method in the class/module where you included it.

For example, if you call the pagy method for a model (that included the Pagy::Method), it would almost certainly not have the request method available.

The simplest way to make it work is as follows:

ruby yourmodel
include Pagy::Method

def self.paginated(view, my_arg1, my_arg2, **)
  collection = ...
  view.instance_eval { pagy(:offset, collection, **) }
end
erb view
<% pagy, records = YourModel.paginated(self, my_arg1, my_arg2, **options) %>

==- Handle POST requests (server side)

You may need to POST a very complex search form and paginate the results. Pagy produces nav tags with GET links, so here is a simple way of handling it.

You can start the process with your regular POST request and cache the filtering data on the server. Then, using the regular GET pagination cycle, pass only the cache key as a param (which avoids sending the actual filters back and forth).

Here is a conceptual example using the session:

ruby
require 'digest'

def filtered_action
  pagy_options = {}
  if params[:filter_key] # retrieve already cached filters
    filters = session[params[:filter_key]]
  else # store new filters
    filters      = params[:filters] # your filter hash
    key          = Digest::SHA1.hexdigest(filters.sort.to_json)
    session[key] = filters

    pagy_options[:querify] = ->(query_hash) { query_hash.merge!(filter_key: key) }
  end
  collection      = Product.where(**filters)
  @pagy, @records = pagy(:offset, collection, **pagy_options)
end

!!!success Notice: ensure a server-side storage
If you use the session for caching, configure it to use ActiveRecord, Redis, or any server-side storage.
!!!

===

{{ include "snippets/ask-for-support" }}


Guides Index.Yml (`docs/guides/index.yml`)

label: Guides
icon: north-star
order: 100
expanded: true


Guides Migration Guide (`docs/guides/migration-guide.md`)


label: Migrate Legacy Pagination icon: paper-airplane order: 80

:icon-paper-airplane:  Migrate WillPaginate/Kaminari


This page covers most of the standard changes you need to make in order to migrate from legacy pagination.

Steps

The Pagy API is quite different from other pagination gems; however, if you split the process into the following general steps, it should be quite simple.

Remove the old code

==- {{ include "snippets/mini-step" step: "1" }} Preparation

  • Uninstall the legacy gem and replace it with gem "pagy" in the Gemfile.
  • Add the include Pagy::Method statement to the application controller.

==- {{ include "snippets/mini-step" step: "2" }} Application-wide search and replace

  • Search for the class name of the legacy gem, for example WillPaginate or Kaminari. You should find most of the code related to the global gem configuration or monkey patching.
  • Remove all the legacy settings of the old gem.

==- {{ include "snippets/mini-step" step: "3" }} Clean up the models

Look for terms like per_page, per, and similar, as these are configuration settings. Include them in the appropriate paginator call in the controller (e.g., pagy(:offset, collection, limit: 10)) or globally in the Pagy initializer (e.g., Pagy::OPTIONS[:limit] = 10).

If the app uses the page scope in model methods or scopes, remove it, along with the argument used to pass the page number, while preserving the rest of the scope. Locate where the app applies this paginated scope in the controllers and replace it with the scope in a paginator statement. For example:

ruby controller
#@records = Product.paginated_scope(params[:page])
@pagy, @records = pagy(:offset, Product.non_paginated_scope)

==- {{ include "snippets/mini-step" step: "4" }} Search and replace in the controllers

In controllers, legacy pagination statements generally map directly to Pagy, making them straightforward to convert.

Search for statements with keywords like page and paginate and use the pagy(:offset, ...) paginator instead. For example:

ruby controller
#@records = Product.some_scope.page(params[:page])
#@records = Product.paginate(:page => params[:page])

@pagy, @records = pagy(:offset, Product.my_scope)

#@records = Product.some_scope.page(params[:page]).per(15)
#@records = Product.some_scope.page(params[:page]).per_page(15)
#@records = Product.paginate(page: params[:page], per_page: 15)

@pagy, @records = pagy(:offset, Product.all, limit: 15)

==- {{ include "snippets/mini-step" step: "5" }} Search and replace in the views

Similarly, in views, legacy pagination statements typically correspond directly to Pagy, simplifying conversion.

Search for statements with keywords like will_paginate and paginate and use one of the series_nav methods. For example:

erb view
<%= will_paginate @records %>
<%= paginate @records %>

<%== @pagy.series_nav %>

===

Find the remaining code

If the app has tests, it's time to run them. If not, start the app and navigate through its pages.

If any legacy code remains, it will raise an exception. Remove the old code and retry until no exceptions occur.

Fine-tuning

Once the app displays pagination correctly, customize Pagy as needed (see Stylesheets).

If the previous pagination used custom elements (e.g., custom params, URLs, links, HTML elements, etc.), adjustments may be required for compatibility. Take a look at the topics in the How To docs: they should cover most of your custom needs.

!!!tip
If the app uses pagination from Bootstrap or Bulma frameworks, the existing CSS should function seamlessly with Pagy navigation helpers.
!!!

{{ include "snippets/ask-for-support" }}


Guides Pagy Next (`docs/guides/pagy-next.md`)


label: Use Pagy NEXT
order: 60
icon: zap
nav:
badge:
text: "≈ 44"
variant: info


:icon-zap:  Pagy NEXT


!!!tip You can use the NEXT version NOW!

Just edit the Gemfile...

diff gemfile (diff)
- gem 'pagy', '~> 43.8'                         # MINOR version restriction
+ gem 'pagy', '~> 43.8.1', require: 'pagy/next' # PATCH version restriction + pagy/next entry point

As an alternative (without Gemfile changes), ensure the environment variable PAGY_NEXT=true is set BEFORE pagy is required.

rb irb
$ PAGY_NEXT=true irb
>> require 'pagy'
=> true
>> Pagy::VERSION
=> "43.8.1.next"

!!!

How does it work?

Pagy NEXT is the code that will be released as the next MAJOR version. It is already implemented and available in the current version; however, it is overridden with the legacy code and deprecation warnings to respect the SemVer contract.

Pagy NEXT provides an opt-in mechanism to bypass the legacy layer entirely. By using require: 'pagy/next' or setting PAGY_NEXT=true, you effectively run the next MAJOR version's code today.

How should you maintain it?

This "early access" mode is lighter and faster, but requires immediate adherence to the latest API changes.

!!!warning Adjust the gem update policy!

  • Use a stricter PATCH version restriction (e.g., replace ~> 43.8 with ~> 43.8.1).
  • Follow the Deprecations Instructions after bundle update involving MINOR or MAJOR releases.
    !!!

With the PATCH version restriction, Bundler will automatically update only PATCH releases, ensuring your NEXT code won't break on update. However, remember to manually check bundle outdated more often to avoid missing MINOR or MAJOR releases.


Guides Quick Start (`docs/guides/quick-start.md`)


label: Quick Start order: 100 icon: rocket

:icon-rocket:  Quick Start


!!!question Migrating from another pagination gem?
Check the Migration Guide first.
!!!

Steps

Install it...

ruby gemfile
gem 'pagy', '~> 43.8' # MINOR version restriction, to avoid breaking changes

Try it...

{{ include "snippets/run-app" app: "demo" }}

Use it...

=== {{ include "snippets/mini-step" step: "1" }} Include the pagy method where you are going to use it (usually ApplicationController):

ruby
include Pagy::Method

=== {{ include "snippets/mini-step" step: "2" }} Use it to paginate any collection with any technique:

ruby
@pagy, @records = pagy(:offset, Product.some_scope, **options) # :offset paginator
@pagy, @records = pagy(:keyset, Product.some_scope, **options) # :keyset paginator
@pagy, @records = pagy(...)

Read the Choose Right guide to pick the right paginators for your app

=== {{ include "snippets/mini-step" step: "3" }} Render navigation tags and other helpers with the @pagy instance methods:

erb
<%# Render navigation bar helpers with various types and styles %>
<%== @pagy.series_nav %>
<%== @pagy.series_nav_js(:bootstrap) %>
<%== @pagy.input_nav_js(:bulma) %>
<%== @pagy.info_tag %>

See all the available @pagy helpers

===


Guides Safety (`docs/guides/safety.md`)


label: Safety icon: shield order: 98

:icon-shield:  Safety


!!!success

Pagy handles the input of its own URL params safely, and assumes that all the configuration, options and arguments you set in your code are safe to use verbatim in its helpers.

!!!

!!!warning WARNING!

If you use any other raw end-user input in your Pagy configuration, options, or arguments, you must sanitize or escape it first in your own code.

!!!


Guides Upgrade Guide (`docs/guides/upgrade-guide.md`)


label: Upgrade to 43
icon: check-circle
order: 70
nav:
badge:
icon: arrow-right
text: "43"
variant: info


:icon-check-circle:  Upgrade to 43


Pagy version 43 is a complete redesign of the legacy code. Its improvements make pagination a lot simpler and more powerful, but require a quite different way to use it.

!!!success Follow this guide to upgrade your app in just a few minutes.
Cherry-pick only what applies to your app: you can safely skip all the rest.
!!!

Steps

Replace the pagy.rb config file...

  • Rename your pagy.rb initializer as pagy-old.rb.
  • Add the new pagy.rb initializer in its place.
  • Cut/remove the Pagy::DEFAULT[...] lines from pagy-old.rb and paste/add them to pagy.rb.
  • Replace all the Pagy::DEFAULT[...] entries with Pagy::OPTIONS[...] in pagy.rb.

In the next steps we will use the pagy-old.rb as the blueprint to guide most of the changes, and we will edit the new pagy.rb as needed.

Remove your used extras...

The new version doesn't use the extras anymore. They got integrated into the core code, and a few have been discontinued.

  • Search for any active require 'pagy/extras/*' in the pagy-old.rb file...
  • When you find one, follow the specific section below to upgrade your code.
  • As you proceed, remove each entry from the pagy-old.rb.

==- Extras

:::
==- array

{.compact}

Search (old) Replace with (new)
pagy_array(...) pagy(:offset,...)

==- arel

{.compact}

Search (old) Replace with (new)
pagy_arel(...) pagy(:offset, count_over: true, ...)

==- pagy

  • All the old helpers are now @pagy instance methods with more explicit names.

{.compact}

Search (old) Replace with (new)
pagy_nav_js(@pagy, ...) @pagy.series_nav_js(...)
pagy_combo_nav_js(@pagy, ...) @pagy.input_nav_js(...)
pagy_limit_selector_js(@pagy, ...) @pagy.limit_tag_js(...)
pagy_prev_url(@pagy, ...) @pagy.page_url(:previous, ...)
pagy_next_url(@pagy, ...) @pagy.page_url(:next, ...)
pagy_prev_a(@pagy, ...) @pagy.previous_tag(...)
pagy_next_a(@pagy, ...) @pagy.next_tag(...)
pagy_prev_link(@pagy, ...) discontinued: implement manually
pagy_next_link(@pagy, ...) discontinued: implement manually
size: ...``Pagy::OPTIONS[:size] = ... slots: ...``Pagy::OPTIONS[:slots] = ...
ends: false``Pagy::OPTIONS[:ends] = false compact: true``Pagy::OPTIONS[:compact] = true

==- bootstrap

  • All the old helpers are now @pagy instance methods with more explicit names.

{.compact}

Search (old) Replace with (new)
pagy_bootstrap_nav(@pagy, ...) @pagy.series_nav(:bootstrap, ...)
pagy_bootstrap_nav_js(@pagy, ...) @pagy.series_nav_js(:bootstrap, ...)
pagy_bootstrap_combo_nav_js(@pagy, ...) @pagy.input_nav_js(:bootstrap, ...)
size: ...``Pagy::OPTIONS[:size] = ... slots: ...``Pagy::OPTIONS[:slots] = ...
ends: false``Pagy::OPTIONS[:ends] = false compact: true``Pagy::OPTIONS[:compact] = true
  • FYI: The redundant pagy-bootstrap class has been removed from the input_nav_js body.

==- bulma

  • All the old helpers are now @pagy instance methods with more explicit names.

{.compact}

Search (old) Replace with (new)
pagy_bulma_nav(@pagy, ...) @pagy.series_nav(:bulma, ...)
pagy_bulma_nav_js(@pagy, ...) @pagy.series_nav_js(:bulma, ...)
pagy_bulma_combo_nav_js(@pagy, ...) @pagy.input_nav_js(:bulma, ...)
size: ...``Pagy::OPTIONS[:size] = ... slots: ...``Pagy::OPTIONS[:slots] = ...
ends: false``Pagy::OPTIONS[:ends] = false compact: true``Pagy::OPTIONS[:compact] = true
  • FYI: The is-centered CSS class has been removed.
  • FYI: The previous/next links have been moved to the beginning and end of the pagination.

==- countless

{.compact}

Search (old) Replace with (new)
pagy_countless(...) pagy(:countless, ...)
countless_minimal: true``Pagy::OPTIONS[:countless_minimal] = true headless: true``Pagy::OPTIONS[:headless] = true

==- calendar

{.compact}

Search (old) Replace with (new)
pagy_calendar(...) pagy(:calendar, ...)
Pagy::Calendar::OutOfRangeError Pagy::RangeError
flag: active: true disabled: false
config: pagy: ... offset: ...
  • If your pagy-old.rb file contains any localization configuration, then uncomment and customize the following line in the pagy.rb initializer: Pagy::Calendar.localize_with_rails_i18n_gem(*your_locales).
    • Note: In non-Rails applications, calendar localization requires adding rails-i18n to your Gemfile.
  • Remove any existing Pagy::Calendar::*::DEFAULT. Pass the options to each unit when you paginate.

==- elasticsearch_rails

  • Active and passive modes are now handled by the same pagy method:

{.compact}

Search (old) Replace with (new)
pagy_elasticsearch_rails(...) pagy(:elasticsearch_rails, ...)
Pagy.new_from_elasticsearch_rails(...) pagy(:elasticsearch_rails, ...)
elasticsearch_rails_search: ...``Pagy::OPTIONS[:elasticsearch_rails_search] = ... search_method: ...``Pagy::OPTIONS[:search_method] = ...
  • Customization of the pagy_search method name has been discontinued:
    • Remove any existing :elasticsearch_rails_pagy_search variable from your code.
    • Replace your custom method name with the standard pagy_search method.

==- meilisearch

  • Active and passive modes are now handled by the same pagy method:

{.compact}

Search (old) Replace with (new)
pagy_meilisearch(...) pagy(:meilisearch, ...)
Pagy.new_from_meilisearch(...) pagy(:meilisearch, ...)
meilisearch_search: ...``Pagy::OPTIONS[:meilisearch_search] = ... search_method: ...``Pagy::OPTIONS[:search_method] = ...
  • Customization of the pagy_search method name has been discontinued:
    • Remove any existing :meilisearch_pagy_search variable from your code.
    • Replace your custom method name with the standard pagy_search method.

==- searchkick

  • Active and passive modes are now handled by the same pagy method:

{.compact}

Search (old) Replace with (new)
pagy_searchkick(...) pagy(:searchkick, ...)
Pagy.new_from_searchkick(...) pagy(:searchkick, ...)
searchkick_search: ...``Pagy::OPTIONS[:searchkick_search] = ... search_method: ...``Pagy::OPTIONS[:search_method] = ...
  • Customization of the pagy_search method name has been discontinued:
    • Remove any existing :searchkick_pagy_search variable from your code.
    • Replace your custom method name with the standard pagy_search method.

==- headers

{.compact}

Search (old) Replace with (new)
pagy_headers(...) @pagy.headers_hash(...)
pagy_headers_merge response.headers.merge!(@pagy.headers_hash)
headers: ...``Pagy::OPTIONS[:headers] = ... headers_map: ...``Pagy::OPTIONS[:headers_map] = ...
  • Notice that the :limit header default is now 'page-limit' (it was 'page-items').
    • Set Pagy::OPTIONS[:headers_map] = { limit: 'page-items', ... } to preserve your current API, if it relies on the old default values.

==- jsonapi

{.compact}

Search (old) Replace with (new)
pagy_jsonapi_links(@pagy, ...) @pagy.urls_hash(...)
  • Notice that the nil links are now removed as the JSON:API specification requires.
  • IMPORTANT: Enable the feature by explicitly setting the jsonapi: true option (in the initializer or pagy method).

==- keyset

{.compact}

Search (old) Replace with (new)
pagy_keyset(...) pagy(:keyset, ...)
pagy_keyset_first_url(@pagy, ...) @pagy.page_url(:first, ...)
pagy_keyset_next_url(@pagy, ...) @pagy.page_url(:next, ...)
filter_newest: ... override compose_predicate
  • Replace any existing :jsonify_keyset_attributes with :pre_serialize.
    • The lambda receives the same keyset_attributes argument, but it must modify the specific values directly. The lambda's return value is ignored. For example: ->(attrs) { attrs[:created_at] = attrs[:created_at].strftime('%F %T.%6N') }.

==- limit

{.compact}

Search (old) Replace with (new)
limit_param: :... (symbol value) limit_key: '...' (string value)
limit_extra: ... delete
client_limit: ... delete
  • Enable the feature by setting client_limit: allowed_max_limit option (in the initializer or pagy method).

==- metadata

{.compact}

Search (old) Replace with (new)
pagy_metadata(@pagy, ...) @pagy.data_hash(...)
metadata: ...``Pagy::OPTIONS[:metadata] = ... data_keys: ...``Pagy::OPTIONS[:data_keys] = ...
data_key -> :scaffold_url :url_template

==- overflow

  • The Pagy::OverflowError has been replaced by the Pagy::RangeError; however, it is no longer raised by default.
  • Pagy rescues the Pagy::RangeError and serves an empty page by default.
    • Now, Pagy behaves the same as it did before when requiring the overflow extra and using its default settings.
  • The legacy pagy.overflow? is now the pagy.in_range? method, which checks/returns the opposite state/boolean.
  • The overflow: :last_page behavior has been discontinued because it provides nearly no benefit:
    • Why is there little benefit in serving the last page?
      • The navigation bar for an out-of-range request is rendered identically to that of the last page.
      • The only difference is that there are no records/results to display.
      • The "previous page" button points to the last page, so if users truly want to see the last page results (which they have likely already seen), they can simply click the link.
  • Summary for keeping the same behavior:
    • The :overflow option is not used anymore.
    • If you did not use the extra (i.e., Pagy raised errors), set raise_range_error: true.
    • If you used overflow: :empty_page or just required the overflow extra, simply remove it (this is now the default behavior).
    • If you used overflow: :last_page and still want this behavior despite the reasons above, see this How To Example.

==- standalone

  • Replace the :url variable with the :request hash option. For example:

    ruby
    request: { base_url: 'http://www.example.com',
               path:     '/path',
               params:   { 'param1' => 1234 }, # The string-keyed params hash from the request
               cookie:   'xyz' }               # The 'pagy' cookie, only for keynav

==- i18n

  • If absolutely necessary, uncomment or add this line to your initializer: Pagy.translate_with_the_slower_i18n_gem!.

==- gearbox (discontinued)

  • Due to extensive overwriting for minimal benefit, you can safely remove this feature from your app without noticeable impact. Remove all /gearbox/ related code.

==- size (discontinued)

  • Pagination bars similar to WillPaginate and Kaminari are not good for a lot of reasons. If still required, adapt the legacy file from a previous commit.

==- trim (discontinued)

  • It was mostly useless and half-baked, causing many complications in both the Ruby and JavaScript code for no significant benefit.
  • Use an appropriate approach to address your requirement, such as utilizing URL rewriting at the HTTP server level.

:::

Update the API...

==- Search and replace

{.compact}

Search (old) Replace with (new)
include Pagy::Backend include Pagy::Method
include Pagy::Frontend (remove: integrated)
pagy_nav(@pagy, ...) @pagy.series_nav(...)
Pagy.root. Pagy::ROOT.
page_param: :... (symbol value) page_key: '...' (string value)
pagy_info(@pagy, ...) @pagy.info_tag(...)
@pagy_locale = ... Pagy::I18n.locale = ...
@pagy.vars @pagy.options
VariableError OptionError
<error>.variable <error>.option
fragment: '#...' (mandatory '#') fragment: '...' (optional '#')
count_args: ... (remove: integrated)
outset: ... (remove: discontinued)
cycle: ... (remove: discontinued)

==- Replace the :params variable...

Use the :querify option, which is a lambda that can modify the string-keyed params hash at will. It is a bit more verbose, but it's more powerful and low-level. It solves an incompatibility with the old high-level :params hash/lambda and improves performance. It is part of the URL Options group that gives you full and efficient control over the URL composition. For example:

ruby
# Old symbol-keyed, high-level hash variable
params: { a: 1, b: 2 }
# New string-keyed, low-level, direct modification of the params hash
querify: ->(p) { p.merge!('a' => 1, 'b' => 2) }
# It also allows things like:
querify: ->(p) { p.except!('not_useful').merge!('custom' => 'useful') }

==- Replace the *prev* abbreviated naming

Use *previous* in all the options, accessors, methods, etc.

===

Finalize the upgrade...

==- JavaScript

If your pagy-old.rb contains any JavaScript setup, it should still work, so you can move it to the pagy.rb file; however, for apps with builders, consider using the new Pagy.sync and removing all the old entries from your JavaScript config files.

==- Stylesheets

The CSS for the default pagy helpers has new selectors and variables. See the new Stylesheets to interactively update your custom CSS.

!!!success CSS Frameworks
Supported CSS frameworks (like Bootstrap and Bulma) don't require any change.
!!!

==- Pagy::I18n and Locale Files

{{ include "snippets/mini-step" step: "1" }} If your pagy-old.rb contains the Pagy::I18n setup, and the setup includes some custom dictionary files, then uncomment and set up the relevant Pagy::I18n lookup section in the pagy.rb file. (See the I18n docs for details)

{{ include "snippets/mini-step" step: "2" }} Update your custom dictionary files (if any) to the new dictionary structure, or they won't work correctly.

{{ include "snippets/mini-step" step: "3" }} Remove all the I18n code from the pagy-old.rb. All the locales are autoloaded when your app uses them.

==- Overriding

  • Overriding methods in controllers/helpers is either impossible or discouraged.
  • The cleanest approach for local overriding is via Ruby refinements or the initializer for global override.
  • Check the How To Override Pagy Methods.
  • If your pagy-old.rb contains overridden methods, copy the methods over to the pagy.rb initializer; however, consider that:
    • Internal Pagy protected methods have been extensively refactored, likely renamed, and occasionally removed.
    • You should reconcile internal overrides by reviewing the updated Pagy codebase.

You may also want to check these internal renamings:

{.compact}

Search (old) Replace with (new)
pagy_url_for(@pagy @pagy.page_url(
pagy_anchor(pagy pagy.a_lambda(
pagy_t Pagy::I18n.translate
label_for page_label
label page_label

==- Delete the pagy-old.rb file

At this point, there should be no more code in the pagy-old.rb.

===

{{ include "snippets/ask-for-support" }}

!!!warning
Please report any issues with this guide by opening a New Docs Issue.
!!!


Resources ARIA (`docs/resources/ARIA.md`)


label: ARIA icon: accessibility order: 60

:icon-accessibility:  ARIA Attributes


Pagy provides a consistent set of ARIA-compliant attributes across all its helpers.

=== :icon-code:  Nav helpers

Pagy provides a customizable aria-label for the root element of its helpers. It is usually a nav element. For the few helper styles that use a different root element, Pagy adds a role="navigation" attribute.

The default string for the aria-label of the root element is "Page" / "Pages" (translated and pluralized according to the total number of pages). This is arguably a better description of the navigation content than just "Pagination" (which is also difficult to translate in certain languages).

!!!danger Don't rely on ARIA default with multiple nav elements!
The nav elements are landmark roles and should be distinctly labeled.
!!!

!!!success Override the default :aria_labels for multiple navs with distinct values!

erb
<%# Explicitly set the aria_label %>
<%== @pagy.series_nav(aria_label: 'Search result pages') %>

!!!

==- :icon-link:  Links

Links to the previous and next pages are rendered as "<" and ">" and are aria-labelled as translated "Previous" and "Next". You can customize both the link text and the aria-label by editing the pagy.previous, pagy.next, pagy.aria_label.previous, and pagy.aria_label.next values in the locale files.

Page links do not include an aria-label attribute because their text is a simple number (read by screen readers in the user's native language), making an explicit attribute redundant and inefficient.

Disabled links include the aria-disabled="true" attribute.

The current page is marked with the aria-current="page" attribute.

The role="link" attribute is applied to link elements in styles that lack an a tag or a href attribute.

The role="separator" is applied to the :gap.

===


Resources I18n (`docs/resources/i18n.md`)


label: I18n icon: globe order: 70

:icon-globe:  Internationalization


Pagy handles string translation and pluralization using its own efficient i18n implementation.

!!!success
Apps using only the English language are supported out of the box without any I18n setup!
!!!

=== :icon-list-ordered:  Setup

All Pagy strings are stored in dictionary files within its locales directory. These files adopt the same structure as the standard locale files for the i18n gem, allowing you to use them seamlessly with both engines.

Pick the I18n engine...

+++ Pagy::I18n

!!!success Recommended: ~18x faster and ~10x lighter
It works seamlessly even if the rest of your app works with the i18n gem.
!!!

Set the Pagy::I18n.locale for each request. For example:

rb controller
before_action { Pagy::I18n.locale = params[:locale] }

Notice: The default Pagy::I18n.locale is 'en'.

+++ I18n gem

!!!warning Slower
!!!

Switch the translation to the i18n gem. Pagy will only provide its dictionaries...

rb [pagy.rb initializer](/toolbox/configuration/initializer)
Pagy.translate_with_the_slower_i18n_gem!

+++

==- :icon-book:  Sample Dictionary

:::code source="/gem/locales/en.yml" title="en.yml":::

==- :icon-pencil:  Customizing dictionaries

!!!warning IMPORTANT
If you use Pagy::I18n, name your custom dictionary as <locale>.yml.
!!!

Place your custom dictionaries in a directory and add its path to the I18n pathnames.

rb [pagy.rb initializer](/toolbox/configuration/initializer)
# Example for Rails:
Pagy::I18n.pathnames << Rails.root.join('config/locales/pagy')

==- :icon-mortar-board:  Contributing a Language

{{ include "snippets/ask-for-support" }}

Find the pluralization of your language...

{{ include "snippets/mini-step" step: "1" }} Locate the file of your locale in the list of pluralizations.

{{ include "snippets/mini-step" step: "2" }} Check the pluralization used in it.

{{ include "snippets/mini-step" step: "3" }} Confirm that Pagy has the same pluralization in the p11n directory.

||| If missing, you have two choices...

{{ include "snippets/mini-step" step: "easy" }} Create a new Feature Request asking for the addition of the pluralization for your locale.

{{ include "snippets/mini-step" step: "best" }} Create a new module for the pluralization (by adapting the same pluralization from the corresponding rails-i18n file) and include tests for it.

|||

Duplicate an existing dictionary...

{{ include "snippets/mini-step" step: "1" }} Pick a (similarly pluralized) dictionary in the gem/locales directory.

{{ include "snippets/mini-step" step: "2" }} Set the p11n entry to the right pluralization module name in the p11n directory.

{{ include "snippets/mini-step" step: "3" }} Provide the necessary plurals for aria_label.nav and item_name.
E.g., with the EastSlavic rule, provide the plurals for one, few, many, and other. With Other, provide only the other value, etc.

==- :icon-location:  Localization

Localization is needed only by the calendar with non-en locales. See the calendar localization.

===


Resources Javascript (`docs/resources/javascript.md`)


label: JavaScript icon: file-code order: 80

:icon-file-code:  JavaScript


!!!tip Add the oj gem to your Gemfile
It is not a requirement, but if present, the pagy JSON generation will be faster.
!!!

Setup

Pick a file...

+++ pagy.mjs

!!!success Good for apps with a minifier (Sprockets, builders, ...)
!!!

Make Pagy available in your JavaScript environment with...

js application.js
import Pagy from "pagy.mjs"

+++ pagy.min.js

!!!success Good for apps without a minifier (Propshaft, Importmaps, ...)
!!!

Make Pagy available in your JavaScript environment with...

erb erb template / html page
<%= javascript_include_tag "pagy.min.js" ...%>


<script src="/path/to/pagy.min.js"></script>

+++ pagy.js

!!!warning Use it only for debugging Pagy itself.
!!!

+++

{{ include "snippets/pick-a-conf" resource: ":javascript" resource_dir: "javascripts" remote_dir: "app/javascript" }}

Run the Pagy.init on...

+++ "load" event

Pick which applies to your environment:

js
window.addEventListener("load", Pagy.init)
window.addEventListener("turbo:load", Pagy.init)
window.addEventListener("turbolinks:load", Pagy.init)
window.addEventListener("your-event", Pagy.init)

+++ Stimulus connect

js
connect() {
  Pagy.init(this.element);
}

+++


Resources Stylesheets (`docs/resources/stylesheets.md`)


label: Stylesheets icon: file order: 90 image: ""

:icon-file:  Stylesheets


Pagy includes a couple of CSS files and the tools to integrate your app's themes interactively.

!!!warning
You don't need any stylesheets if you use the pagy :bootstrap or :bulma helpers and styles.
!!!

Setup

Pick a file...

+++ pagy.css

!!!success Good for any app
!!!

==- CSS Source

:::code source="/gem/stylesheets/pagy.css" title="pagy.css":::

===

+++ pagy-tailwind.css

!!!warning Works only with apps using Tailwind
!!!

==- CSS Source

:::code source="/gem/stylesheets/pagy-tailwind.css" title="pagy-tailwind.css":::

===

+++

{{ include "snippets/pick-a-conf" resource: ":stylesheet" resource_dir: "stylesheets" remote_dir: "app/assets/stylesheets" }}

Customize the style...

Add this line to any template/layout <head> while developing:

erb
<%== Pagy.dev_tools %>

and adjust a few sliders to see the change in real time, right in your app, with the Pagy Wand. Then copy the CSS Override field and paste it into your own CSS.

:::raised
{width=300}
:::

==- :icon-key-asterisk:  Selectors

To ensure a minimalistic valid output, complete with all the ARIA attributes, pagy outputs a single line with the minimum number of tags and attributes required to identify all the parts of the nav bars:

  • The output of the series_nav and series_nav_js helpers is a series of a tags inside a nav tag wrapper.
  • The disabled links are simply missing the href attribute.
  • The pagy series-nav and pagy series-nav-js classes are assigned to the nav tag.

{{ include "snippets/run-app" app: "demo" }}

!!!tip

  • You can target the gap with .pagy a[role="separator"]
  • You can target the previous and next links by using .pagy a:first-child and .pagy a:last-child pseudo-classes.
  • Check the stylesheet comments to target other specific elements.
    !!!

==- :icon-arrow-left:  RTL

The pagy stylesheets automatically support Right-To-Left (RTL) languages. It respects the standard dir="rtl" attribute of the html tag or any parent element of the pagination.

===


Resources Urls (`docs/resources/urls.md`)


label: URLs icon: crosshairs order: 50

:icon-crosshairs:  URLs


Pagy uses the current URL as the base to generate all the other page URLs. It retrieves it from the self.request when available, or from a :request option set to a Rack::Request or a simple Hash of :base_url, :path, :params (and a pagy :cookie value in case of Keynav pagination).

!!!primary
Pagy generates its specialized URLs ~20x faster than generic helpers like Rails' url_for. When possible, it just replaces the page value, without recalculating every part of a URL.
!!!

==- :icon-sliders:  Options

These options give you full control over the URL composition for paginators and helpers:

:::

=== Consumed by Paginators

{{ include "options/paginator-url" }}

=== Consumed by Helpers

{{ include "options/helper-url" }}

:::

==- :icon-blocked:  Params

!!!danger No framework params!
Pagy ignores the request.params because they are modified differently by different frameworks (Rails, Sinatra, etc.). It can only rely on the request.GET and request.POST methods, which are consistent across all Rack environments.
!!!

!!!danger No symbolic params!
The params that Pagy handles when composing its URLs are strictly string-keyed. That's how every gem/framework handles URLs, because it avoids the maintenance and performance overhead of converting symbols back and forth during URL composition.
!!!

==- :icon-download:  GET

Serving paginated collections is a retrieval action that does not modify data; therefore, it should rely on GET requests. Since most applications follow this standard, Pagy parses and produces GET URLs out of the box.

==- :icon-upload:  POST

If you must use POST to retrieve paginated collections, you should build your own POST forms/requests using the data provided by Pagy (e.g., via the data_hash or instance readers). However, Pagy parses and handles POST request parameters out of the box without additional configuration.

==- :icon-stop:  Dynamic Segments

Routers (like the Rails router) allow defining parameters as part of the path (e.g., /items/:page)...

!!!danger
Pagy neither supports nor recommends dynamic path segments for the :page param.
!!!

Why?

The Cons are overwhelming.

:icon-thumbsup:  Pros

{.list-icon}

  • • Aesthetically cleaner URLs
  • • Possibility to cache single pages at the edge (rarely necessary)

:icon-thumbsdown:  Cons

{.list-icon}

  • RFC 3986 Compliance
    • • The :page parameter represents non-hierarchical data, which fits the definition of the query string component.
    • • It does not fit the definition of the path component, which represents hierarchical resources.
  • Data Identification
    • • A query string parameter is labeled data (?page=2) identifiable without external context.
    • • A path segment (/2) is unlabeled and relies on external routing logic to be handled, so it cannot be identified/used by agnostic code.
  • Performance
    • • Dynamic segments are framework-specific routing concepts, not query params concepts.
    • • Using framework code is not only non-agnostic, but significantly slower than pagy's generic query param handling.
    • • Using it (or even just checking for it) would be an unnecessary burden for all the apps.

:::
==- OK, but what if I still want it in my own app?

!!!danger This file is provided as a courtesy configuration
It is not an officially supported or tested feature, as it bypasses the standard, high-performance Pagy URL generation logic.

Use it at your own risk and extra maintenance!
!!!

:::code source="/gem/apps/enable_rails_page_segment.rb" title="enable_rails_page_segment.rb":::

:::


Sandbox Console (`docs/sandbox/console.md`)


label: Console icon: terminal order: 90

:icon-terminal:  Console


Allows you to interact with Pagy in an irb or pry environment without an app, providing a pre-configured stubbed environment for you.

You can use a few methods to create a simple collection paginated with the :offset paginator or to set the parameters as needed.

text
/* Detailed source-code truncated for AI context efficiency. */

Sandbox Dev Tools (`docs/sandbox/dev_tools.md`)


label: DevTools icon: ai-model order: 80

:icon-ai-model:  DevTools


Pagy offers a couple of tools to ease your development process.

You can activate them by adding this line in the <head> of your layout/page:

erb
<%== Pagy.dev_tools %>

:::content-center
{{ include "snippets/run-app" app: "demo" }}
:::

:icon-north-star:  Pagy Wand

Adjust a few sliders to see the style changing in real time, right in your app...

  • Control most of the visual aspects of pagy with a few presets and sliders.
  • Copy/paste the generated "CSS Override" block into your stylesheet to persist it in your app.
  • Click on the :icon-question: icon on the Wand for help.

:::raised
{width=450}
:::

Watch the Pagy Wand demo video:

:::raised
{width=450}
:::

:icon-sparkle:  Pagy AI

Get answers to your specific questions, right in your app. Just type questions like: "What are the available paginators for DBs and search platforms?"


Sandbox Playground (`docs/sandbox/playground.md`)


label: Playground order: 100 icon: play

:icon-play:  Playground


You can showcase, clone, and develop a few Pagy apps without needing to set anything up on your side!

sh
$ pagy --help

:icon-apps:  Apps

We have a few single-file apps ready to run in your browser for various purposes. Most of them are used to run the E2E Test workflow.

==- :icon-pulse:  Repro (interaction example) {#repro}

You can use this app as a starting point to try Pagy or reproduce issues to get support or file bug reports.

Clone the app

sh
pagy clone repro

You should find the ./repro.ru cloned app file in the current directory. Feel free to rename or move it as you wish.

Develop it

This command runs your rackup app with a puma server.

sh
pagy path/to/your-repro.ru

Open a browser and navigate to http://127.0.0.1:8000

==- :icon-ruby:  Rails {#rails}

Use it to reproduce Rails-related Pagy issues.

sh
pagy clone rails
pagy ./rails.ru

==- :icon-search:  Active Search {#active-search}

The interactive showcase for the :rails_active_search paginator, in active and passive modes.

sh
pagy active_search

==- :icon-eye:  Demo {#demo}

The interactive showcase for all the pagy helpers and CSS styles.

sh
pagy demo

==- :icon-calendar:  Calendar {#calendar}

The interactive showcase and reproduction tool for the :calendar paginator.

sh
pagy calendar

==- :icon-key:  Keyset / Keynav {#keysets}

The interactive showcases/repros for the :keyset and :keynav_js paginators with ActiveRecord or Sequel sets, and for multiple independent :keynav_js instances (keynav+root_key):

sh
pagy --help | grep key
  keynav+root_key    Showcase the Keynav pagination with independent instances
  keynav             Showcase the Keynav pagination (ActiveRecord example)
  keyset             Showcase the Keyset pagination (ActiveRecord example)
  keyset_sequel      Showcase the Keyset pagination (Sequel example)

pagy keynav
pagy keynav+root_key
pagy keyset
pagy keyset_sequel

===

==- :icon-stop:  Troubleshooting

All the pagy apps use bundler/inline, which should be able to install all the gems automatically at app startup.

Depending on your environment, you might get this message for some gem:

txt
You have already activated GEMNAME v1, but your Gemfile requires GEMNAME v2.
Prepending `bundle exec` to your command may solve this.

If bundle exec doesn't solve it, then try bundle update and gem cleanup. If you encounter another error after that:

txt
... `find_spec_for_exe': can't find gem GEMNAME (>= 0.x) with executable EXEC (Gem::GemNotFoundException)

then gem pristine GEMNAME should solve the problem.

===


Toolbox Helpers (`docs/toolbox/helpers.md`)


label: " ✳   Helpers" icon: mention order: 80

✳  Helpers


The @pagy instance provides all the helpers to use in your code.

Its class is determined by the paginator used, but you can safely ignore it.

!!!success The @pagy helpers are autoloaded only if used!
Unused code consumes no memory.
!!!


Toolbox Index.Yml (`docs/toolbox/index.yml`)

label: Toolbox
icon: package
order: 80
expanded: true


Toolbox Paginators (`docs/toolbox/paginators.md`)


label: " ✳   Paginators" icon: database order: 90

✳  Paginators


||| The pagy Method

The pagy method provides a common interface to all paginators. Include it where you are going to paginate a collection (usually in ApplicationController):

ruby
include Pagy::Method

You can use it to paginate ANY collection, with ANY technique. For example:

ruby
@pagy, @records = pagy(:offset, collection, **options)
@pagy, @records = pagy(:keyset, set, **options)
@pagy, @records = pagy(...)
  • :offset, :keyset, etc. are symbols identifying the paginator. They implement the specific pagination.
  • @pagy is the pagination instance. It provides all the instance helper methods to use in your code.
  • @records are the records belonging to the requested page.

!!!info
The pagy method expects to find the rack request at self.request; however, you can also use pagy outside controllers or views, or pass your own :request option.
!!!

|||

Paginators

!!!tip Also read the Choose Right guide to ensure good performance and a smooth workflow.
!!!

The paginators are symbolic names of different pagination types/contexts (e.g., :offset, :keyset, :countless, etc.). You pass the name to the pagy method and pagy will internally instantiate and handle the appropriate paginator class.

!!!warning Avoid instantiating Pagy classes directly
Instantiate paginator classes only if the documentation explicitly suggests it.
!!!

!!!success Paginators and classes are autoloaded only if used!
Unused code consumes no memory.
!!!

==- :icon-stop:  Troubleshooting

:::
==- Records may repeat in different pages or be missing

||| :icon-question: Unordered PostgreSQL collection

PostgreSQL collections must be ordered.

||| :icon-check-circle: Solutions

Chain something like .order(:id) to your collection. See the PostgreSQL Documentation

|||
:::
===


Includes Options Helper Url (`docs/_includes/options/helper-url.md`)

absolute: true
: Makes the URL absolute.

path: '/my_path'
: Overrides the request path in pagination URLs. Use the path only (not the absolute URL). (see Override the request path)

fragment: '...'
: URL fragment string.

querify: tweak
: Set it to a lambda to directly edit the passed string-keyed params hash itself. Its result is ignored.

ruby
tweak = ->(q) { q.except!('not_useful').merge!('custom' => 'useful') }

Includes Options Helper (`docs/_includes/options/helper.md`)

anchor_string: 'data-turbo-frame="paginate"'
: Concatenate a verbatim raw string to the internal HTML of the anchor tags. It must contain properly formatted HTML attributes. It's not suitable for *_hash helpers.

{{ include "helper-url" }}


Includes Options Keyset (`docs/_includes/options/keyset.md`)

keyset: {...}
: Set it only to force the keyset hash of column/order pairs. (It is set automatically from the set order)

tuple_comparison: true
: Enable the tuple comparison e.g. (brand, id) > (:brand, :id). It works only with the same direction order, hence, it's ignored for mixed order. Check how your DB supports it (your keyset should include only NOT NULL columns).

pre_serialize: serialize
: Set it to a lambda that receives the keyset_attributes hash. Modify this hash directly to customize the serialization of specific values (e.g., to preserve Time object precision). The lambda's return value is ignored.
ruby serialize = lambda do |attributes| # Convert it to a string matching the stored value/format in SQLite DB attributes[:created_at] = attributes[:created_at].strftime('%F %T.%6N') end

{{ include "paginator" }}


Includes Options Navs (`docs/_includes/options/navs.md`)

id: 'my-nav'
: Set the id HTML attribute of the nav tag.

aria_label: 'My Label'
: Override the default pagy.aria_label.nav string of the aria-label attribute.See ARIA.

!!!danger
The nav elements are landmark roles and should be distinctly labeled!
!!!

!!!success
Override the default :aria_labels for multiple navs with distinct values!

erb
<%# Explicitly set the aria_label %>
<%== @pagy.series_nav(aria_label: 'Search result pages') %>

!!!


Includes Options Offset (`docs/_includes/options/offset.md`)

count_over: true
: Use this option with GROUP BY collections to calculate the total number of results using COUNT(*) OVER ().

raise_range_error: true
: Enable the Pagy::RangeError (which is otherwise rescued to an empty page by default).

{{ include "paginator" }}


Includes Options Paginator Url (`docs/_includes/options/paginator-url.md`)

jsonapi: true
: Enables JSON:API-compliant URLs with nested query string (e.g., ?page[number]=2&page[size]=100).

root_key: 'my_root'
: Set it to enable nested URLs with nested query string ?my_root[page]=2&my_root[limit]=100. Use it to handle multiple pagination objects in the same request.

page_key: 'my_page'
: Set it to change the key string used for the :page in URLs (default 'page').

limit_key: 'my_limit'
: Set it to change the key string used for the :limit in URLs (default 'limit').


Includes Options Paginator (`docs/_includes/options/paginator.md`)

limit: 10
: Specifies the number of items per page (default: 20).

client_limit: 200
: Allow the client to request a :limit up to :client_limit. A higher requested :limit is silently capped.

IMPORTANT If falsey or zero, the client cannot request any :limit.

page: force_page
: Set it only to force the current :page. (It is set automatically from the request param)

request: request || hash
: Pagy tries to find the Rack::Request at self.request. Set it only when it's not directly available in your code (e.g., Hanami, standalone app, test,...). For example:
ruby hash_request = { base_url: 'http://www.example.com', path: '/path', params: { 'param1' => 1234 }, # The string-keyed params hash from the request cookie: 'xyz' } # The 'pagy' cookie, only for keynav

{{ include "paginator-url" }}


Includes Options Series (`docs/_includes/options/series.md`)

slots: 9
: Override the default number of page :slots used for the navigation bar.

  • slots < 7 fills the slots with contiguous pages around the current one.
  • slots >= 7 reserves the first and last slots for the first and last pages, separated from the rest by a ... (:gap) slot when needed.
  • Prefer odd numbers of slots, which place the current page in the central slot.

compact: true
: Fill all the slots with contiguous pages, regardless of the number of slots.


Includes Snippets All But Keyset (`docs/_includes/snippets/all-but-keyset.md`)

!!!warning
{{ $.what | object.default "It" }} works with all paginators but :keyset.
!!!


Includes Snippets Ask For Support (`docs/_includes/snippets/ask-for-support.md`)

!!!question

Feel free to ask for further help via Pagy Support.

!!!


Includes Snippets Js Required (`docs/_includes/snippets/js-required.md`)

  !button variant="info" icon="alert" size="s" corners="pill" text="JavaScript Setup Required!"


Includes Snippets Mini Step (`docs/_includes/snippets/mini-step.md`)

[!badge variant="info" size="s" corners="pill" text="{{ $.step }}"] 


Includes Snippets Nav Styles (`docs/_includes/snippets/nav-styles.md`)

==- :icon-eye:  Styles

:pagy/nil
: Pagy default style

:bootstrap
: Set classes: 'pagination pagination-sm any-class' style option to override the default 'pagination' class.

:bulma
: Set classes: 'pagination is-small any-class' style option to override the default 'pagination' classes.


Includes Snippets Offset Readers (`docs/_includes/snippets/offset-readers.md`)

offset
: The OFFSET used in the SQL query.

count
: The collection count.

from
: The position in the collection of the first item on the page. (Different Pagy classes may use different value types for it).

to
: The position in the collection of the last item on the page. (Different Pagy classes may use different value types for it).

last
: The last page.

pages
: The number of pages.

previous
: The previous page.

{{ include "paginator-readers" }}


Includes Snippets Paginator Readers (`docs/_includes/snippets/paginator-readers.md`)

next
: The next page.

page
: The current page.

limit
: The items per page.

in
: The actual items in the page.

records
: The fetched records for the current page.

options
: The hash of options of the object.


Includes Snippets Pick A Conf (`docs/_includes/snippets/pick-a-conf.md`)

Pick a configuration...

+++ Sync

!!!success Works with any app
!!!

The following statement will copy and keep synced your picked pagy* file in your own asset dir, so it will be processed like an asset of your app.

rb [pagy.rb initializer](/toolbox/configuration/initializer)
# Replace 'pagy*' with the file you picked
Pagy.sync({{ $.resource }}, Rails.root.join('{{ $.remote_dir }}'), 'pagy*') if Rails.env.development?

==- Sync Task

If you prefer to sync manually or during an automation step, you can define your own task with a single line in the Rakefile, or any *.rake file:

rb
# Pagy::SyncTask.new(resource, destination, *targets)
# Replace 'pagy*' with the file you picked
Pagy::SyncTask.new({{ $.resource }}, Rails.root.join('{{ $.remote_dir }}'), 'pagy*')

and exec it with...

sh
bundle exec rake pagy:sync{{ $.resource }}

===

+++ Pipeline

!!!warning Works only with apps with an assets pipeline
!!!

rb
Rails.application.config.assets.paths << Pagy::ROOT.join('{{ $.resource_dir }}')

+++


Includes Snippets Run App (`docs/_includes/snippets/run-app.md`)

[!button corners="pill" variant="info" icon="play-24" text="Check it out with bundle exec pagy {{ $.app }}"](/sandbox/playground/#{{ $.anchor | object.default $.app }})


Toolbox Configuration Initializer (`docs/toolbox/configuration/initializer.md`)


label: Initializer icon: gear order: 80

:icon-gear:  Initializer


Set your global options/defaults, or configure a few special features by editing the sample file below.

Ensure it loads by saving it into the Rails config/initializers directory, or by requiring it.

!file pagy.rb

:::code source="/gem/config/pagy.rb" title="pagy.rb" language="ruby":::


Toolbox Configuration Options (`docs/toolbox/configuration/options.md`)


label: "Options" icon: sliders order: 100

:icon-sliders:  Options Hierarchy


!!! This page documents the options hierarchy
The actual options are documented alongside the paginators and helpers that consume them.
!!!

Pagy implements a hierarchical options system working at three different levels, regardless of where the option gets consumed.

Levels

Global

  • For example, Pagy::OPTIONS[:limit] = 10 set in the pagy.rb initializer.
  • The Pagy::OPTIONS are inherited by all paginators and helpers.
  • Good practice: Call Pagy::OPTIONS.freeze after you set your Pagy::OPTIONS.

Paginator

  • For example, pagy(paginator, collection, **options).
  • The options passed to a paginator override the Pagy::OPTIONS for that instance.
  • They are also inherited by all the helpers used by the instance.

Helper

  • For example, @pagy.series_nav(**options).
  • The options passed to a helper override the options affecting its output.
  • The options consumed upstream are not affected.

!!!success

  • Keep options local
    • For clarity, set options as close to where they're consumed as possible.
  • Widen the scope when needed
    • Use higher-level options when they conveniently affect broader scopes.
      !!!

Toolbox Helpers Anchor Tags (`docs/toolbox/helpers/anchor_tags.md`)


label: anchor_tags icon: code-square order: 140 image: ""

:icon-code-square:  anchor_tags


:::raised
{width=73}
:::

:::content-center
{{ include "snippets/run-app" app: "demo" }}
:::

The previous_tag and next_tag return the enabled/disabled previous/next page anchor tag.

Useful to build minimalistic UIs that don't use nav bar links (e.g., :countless, :keyset paginators).

!!!success
next_tag works with all paginators
!!!

{{ include "snippets/all-but-keyset" what: "previous_tag" }}

=== :icon-tools:  Usage

erb
<%== @pagy.next_tag %>
<%== @pagy.previous_tag %>

==- :icon-pin:  Examples

ruby console
require 'pagy/console'
=> true

>> puts @pagy.previous_tag
<a href="/path?example=123&amp;page=2" rel="prev" aria-label="Previous">&lt;</a>
=> nil

>> puts @pagy.next_tag
<a href="/path?example=123&amp;page=4" rel="next" aria-label="Next">&gt;</a>
=> nil

>> puts @pagy.next_tag(text: 'Show Next', aria_label: 'my-next')
<a href="/path?example=123&amp;page=4" rel="next" aria-label="my-next">Show Next</a>
=> nil

==- :icon-sliders:  Options

text: 'My Page'
: Override the default text (instead of looking up the pagy.previous/pagy.next entry in the dictionary).

aria_label: 'My Link'
: Override the default aria label (instead of looking up the pagy.aria_label.previous/pagy.aria_label.next entry in the dictionary).

{{ include "options/helper-url" }}

===


Toolbox Helpers Data Hash (`docs/toolbox/helpers/data_hash.md`)


label: data_hash icon: list-unordered order: 200

:icon-list-unordered:  data_hash


data_hash plucks a hash of key-value pairs from the pagy object. It is useful for exporting pagination data to JavaScript frameworks like Vue.js, React.js, etc.

!!!success It works with all paginators
!!!

=== :icon-tools:  Usage

ruby controller
@pagy, @records = pagy(:offset, collection, **options)
pagy_hash       = @pagy.data_hash(data_keys: %i[page previous next previous_url next_url ...])
#=> { page: 3, previous: 2, next: 4, previous_url: ... }
render json: { data: @records, pagy: pagy_hash }

==- :icon-pin:  Examples

ruby console
require 'pagy/console'
=> true

>> @pagy, @records = pagy(:offset, collection.new, page: 3)
=> [#<Pagy::Offset:0x00007f0802c9bd80 @request=#<Pagy::Request:0x00007f0802c858c8 @options={page: 3, request: #<Pagy::Request:0x00007f0802c858c8 ...>, client_limit: nil, limit: 20, count: 1000}, @base_url="http://www.example.com", @path="/path", @params={example: "123"}, @cookie=nil>, @options={limit: 20, limit_key: "limit", page_key: "page", page: 3, client_limit: nil, count: 1000}, @limit=20, @count=1000, @page=3, @last=50, @offset=40, @in_range=true, @from=41, @to=60, @in=20, @previous=2, @next=4>, [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]]

>> @pagy.data_hash(data_keys: %i[page previous next previous_url next_url])
=> {page: 3, previous: 2, next: 4, previous_url: "/path?example=123&page=2", next_url: "/path?example=123&page=4"}

==- :icon-sliders:  Options

data_keys: %i[...]
: For efficiency, always set the :data_keys option to restrict the output to ONLY the keys you need among the default list:

  • :count
  • :current_url
  • :first_url
  • :from
  • :in
  • :last
  • :last_url
  • :limit
  • :next
  • :next_url
  • :options
  • :page
  • :page_url (alias of :current_url)
  • :pages
  • :previous
  • :previous_url
  • :series
  • :to
  • :url_template

Notice that you can also add other @pagy instance method names not included in the default list.

{{ include "options/helper-url" }}

==- :icon-tools:  Usage of :url_template

This is a URL string containing the "P " page token as a placeholder for the page value.

For example: '/foo?page=P &bar=baz'.

Replace it with JavaScript to generate the actual page URLs:

javascript
pageUrl = url_template.replace('P ', '123')
// Result: '/foo?page=123&bar=baz'

!!!warning You may not need it for simple cases!
Consider using the available :*_url data keys directly instead of relying on the :url_template.
!!!

===


Toolbox Helpers Headers Hash (`docs/toolbox/helpers/headers_hash.md`)


label: headers_hash icon: list-unordered order: 190

:icon-list-unordered:  headers_hash


headers_hash generates the standard link header defined in RFC-8288, and adds 4 customizable headers useful for pagination, which you can merge into the response.headers.

It also adheres to the header casing introduced by rack version 3+ (see the rack-issue).

!!!success It works with all paginators
!!!

=== :icon-tools:  Usage

rb controller
# Any paginator will work
@pagy, @records = pagy(:offset, collection, **options)

# Merge the headers to the response
response.headers.merge!(@pagy.headers_hash)
render json: @records

==- :icon-pin:  Examples

ruby console
require 'pagy/console'
=> true

>> @pagy, @records = pagy(:offset, collection.new, page: 3)
=> [#<Pagy::Offset:0x00007f0802c9a020 @request=#<Pagy::Request:0x00007f0802c84e28 @options={page: 3, request: #<Pagy::Request:0x00007f0802c84e28 ...>, client_limit: nil, limit: 20, count: 1000}, @base_url="http://www.example.com", @path="/path", @params={example: "123"}, @cookie=nil>, @options={limit: 20, limit_key: "limit", page_key: "page", page: 3, client_limit: nil, count: 1000}, @limit=20, @count=1000, @page=3, @last=50, @offset=40, @in_range=true, @from=41, @to=60, @in=20, @previous=2, @next=4>, [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]]

>> @pagy.headers_hash(absolute: true)
=> {"link" => "<http://www.example.com/path?example=123>; rel=\"first\", <http://www.example.com/path?example=123&page=2>; rel=\"prev\", <http://www.example.com/path?example=123&page=4>; rel=\"next\", <http://www.example.com/path?example=123&page=50>; rel=\"last\"", "current-page" => "3", "page-limit" => "20", "total-count" => "1000", "total-pages" => "50"}
text example of default output
link <https://example.com:8080/foo?page=1>; rel="first", <https://example.com:8080/foo?page=2>; rel="prev",
<https://example.com:8080/foo?page=4>; rel="next", <https://example.com:8080/foo?page=50>; rel="last"
current-page 3
page-limit 20
total-pages 50
total-count 1000

==- :icon-sliders:  Options

headers_map: {...}
: Customize the headers:

text
```rb
default_map = { page:  'current-page',
                limit: 'page-limit',
                count: 'total-count',
                pages: 'total-pages' }

# Optional customization
headers_map = { page:  'current-page',
                limit: 'per-page',
                pages: false,  # disable the output
                count: 'total' }
headers = @pagy.headers_hash(headers_map:)
# Note: You can also pass the `:headers_map` option to the paginator
```

{{ include "options/helper-url" }}

==- :icon-light-bulb:  Suggestions

Instead of explicitly merging the headers before each rendering...

If you use Rails, you can add an after_action to your application controller:

ruby controller (after_action)
# It merges the headers if `@pagy` is initialized
after_action { response.headers.merge!(@pagy.headers_hash) if @pagy }

# Paginate normally in your action
@pagy, records = pagy(:offset, collection, **options)
render json: records

If your code is consistent across different actions...

You can encapsulate the statements in a custom pagy_render method in your application controller. For example:

ruby controller (method)
def pagy_render(collection, **)
  pagy, records = pagy(:offset, collection, **) # Any other paginator works as well
  response.headers.merge!(pagy.headers_hash)    # Adds pagination headers to the response
  render json: records
end

# And use it in your standard actions:
pagy_render(collection, **options)

===


Toolbox Helpers Info Tag (`docs/toolbox/helpers/info_tag.md`)


label: info_tag icon: code-square order: 120 image: ""

:icon-code-square:  info_tag


:::raised
Displaying items 201-220 of 1000 in total
:::

:::content-center
{{ include "snippets/run-app" app: "demo" }}
:::

info_tag provides information about the content of the current pagination.

{{ include "snippets/all-but-keyset" }}

=== :icon-tools:  Usage

erb
<%== @pagy.info_tag(**options) %>

==- :icon-pin:  Examples

ruby console
require 'pagy/console'
=> true

>> puts @pagy.info_tag # @pagy generated by countless/keynav doesn't have count
<span class="pagy info">Page 3 of 4</span>
=> nil

>> puts @pagy.info_tag
<span class="pagy info">Displaying items 41-60 of 1000 in total</span>
=> nil

>> puts @pagy.info_tag(id: 'the-info-tag', item_name: 'Products')
<span id="the-info-tag" class="pagy info">Displaying Products 41-60 of 1000 in total</span>
=> nil

==- :icon-sliders:  Options

id: 'my-info'
: The id HTML attribute of the span tag wrapping the info.

item_name: 'Products'
: The already pluralized string that will be used in place of the default item/items.

===


Toolbox Helpers Input Nav Js (`docs/toolbox/helpers/input_nav_js.md`)


label: input_nav_js icon: code order: 150 image: ""

:icon-code:  input_nav_js{{ include "snippets/js-required" }}


:::raised
{width=204}


{width=178}


{width=201}
:::

:::content-center
{{ include "snippets/run-app" app: "demo" }}
:::

input_nav_js combines navigation and pagination info in a single compact element.

It is the fastest and lightest navigator, recommended when you care about efficiency and server load while still needing a UI (see Maximize performance).

{{ include "snippets/all-but-keyset" }}

=== :icon-tools:  Usage

erb
<%== @pagy.input_nav_js(**options) %>  <%# default pagy style %>
<%== @pagy.input_nav_js(:bootstrap, **options) %>
<%== @pagy.input_nav_js(:bulma, **options) %>

==- :icon-pin:  Examples

text
/* Detailed source-code truncated for AI context efficiency. */

{{ include "snippets/nav-styles" }}

==- :icon-sliders:  Options

{{ include "options/navs" }}

{{ include "options/helper" }}

==- :icon-alert:  Caveats

!!!danger Overriding *_js helpers is not recommended
The *_js helpers are tightly coupled with the JavaScript code, so any partial overriding on one side would be quite fragile and might break in future releases.
!!!

===


Toolbox Helpers Limit Tag Js (`docs/toolbox/helpers/limit_tag_js.md`)


label: limit_tag_js icon: code-square order: 130 image: ""

:icon-code-square:  limit_tag_js{{ include "snippets/js-required" }}


:::raised
{width=202}
:::

:::content-center
{{ include "snippets/run-app" app: "demo" }}
:::

The limit_tag_js allows the user to select any arbitrary limit per page, up to the :client_limit option. It raises a Pagy::OptionError exception if the :client_limit is not truthy.

{{ include "snippets/all-but-keyset" }}

=== :icon-tools:  Usage

erb
<%== @pagy.limit_tag_js %>

!!!success Pagy requests the right page number
After selecting a new limit, pagy reloads the page that roughly contains the same items shown before the reload.
!!!

==- :icon-pin:  Examples

ruby console
require 'pagy/console'
=> true

>> puts @pagy.limit_tag_js(client_limit: 100)
<span class="pagy limit-tag-js" data-pagy="WyJsdGoiLDQxLCIvcGF0aD9leGFtcGxlPTEyMyZwYWdlPVAgIiwiUCAiLCJMICJd"><label>Show <input name="limit" type="number" min="1" max="100" value="20" style="padding: 0; text-align: center; width: 3rem;"><a style="display: none;">#</a> items per page</label></span>
=> nil

>> puts @pagy.limit_tag_js(client_limit: 100, id: 'my-elector', item_name: 'Products')
<span id="my-elector" class="pagy limit-tag-js" data-pagy="WyJsdGoiLDQxLCIvcGF0aD9leGFtcGxlPTEyMyZwYWdlPVAgIiwiUCAiLCJMICJd"><label>Show <input name="limit" type="number" min="1" max="100" value="20" style="padding: 0; text-align: center; width: 3rem;"><a style="display: none;">#</a> Products per page</label></span>
=> nil

==- :icon-sliders:  Options

{{ include "options/helper-url" }}

==- :icon-alert:  Caveats

!!!danger Overriding *_js helpers is not recommended
The *_js helpers are tightly coupled with the JavaScript code, so any partial overriding on one side would be quite fragile and might break in future releases.
!!!

===


Toolbox Helpers Page Url (`docs/toolbox/helpers/page_url.md`)


label: page_url icon: diff-renamed order: 175

:icon-diff-renamed:  page_url


page_url returns the URL of any page of any instance. If the page is not available, it returns nil. It is useful to build minimalistic UIs that don't use nav bar links (e.g., :keyset paginator).

!!!success It works with all paginators
!!!

=== :icon-tools:  Usage

ruby
@pagy.page_url(:next)
@pagy.page_url(23, page_key: 'custom_page')
...

==- :icon-pin:  Examples

ruby console
require 'pagy/console'
=> true

>> @pagy, @records = pagy(:offset, collection.new, page: 3)
=> [#<Pagy::Offset:0x00007f0802af62c8 @request=#<Pagy::Request:0x00007f0802faf238 @options={page: 3, request: #<Pagy::Request:0x00007f0802faf238 ...>, client_limit: nil, limit: 20, count: 1000}, @base_url="http://www.example.com", @path="/path", @params={example: "123"}, @cookie=nil>, @options={limit: 20, limit_key: "limit", page_key: "page", page: 3, client_limit: nil, count: 1000}, @limit=20, @count=1000, @page=3, @last=50, @offset=40, @in_range=true, @from=41, @to=60, @in=20, @previous=2, @next=4>, [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]]

>> @pagy.page_url(:first)
=> "/path?example=123"

>> @pagy.page_url(:current)
=> "/path?example=123&page=3"

>> @pagy.page_url(:page) # alias of :current
=> "/path?example=123&page=3"

>> @pagy.page_url(:previous)
=> "/path?example=123&page=2"

>> @pagy.page_url(:next)
=> "/path?example=123&page=4"

>> @pagy.page_url(:last)
=> "/path?example=123&page=50"

>> @pagy.page_url(23, page_key: 'custom_page')
=> "/path?example=123&custom_page=23"

>> @pagy.page_url(23, absolute: true)
=> "http://www.example.com/path?example=123&page=23"

>> @pagy.page_url('long-page-id', absolute: true)
=> "http://www.example.com/path?example=123&page=long-page-id"

==- :icon-sliders:  Options

{{ include "options/helper-url" }}

===


Toolbox Helpers Series Nav (`docs/toolbox/helpers/series_nav.md`)


label: series_nav icon: code order: 170 image: ""

:icon-code:  series_nav


:::raised
{width=288}


{width=248}


{width=304}
:::

:::content-center
{{ include "snippets/run-app" app: "demo" }}
:::

series_nav returns an HTML string containing pagination links, wrapped in a nav tag, ready to be used in your view.

{{ include "snippets/all-but-keyset" }}

=== :icon-tools:  Usage

erb
<%== @pagy.series_nav(**options) %>  <%# default pagy style %>
<%== @pagy.series_nav(:bootstrap, **options) %>
<%== @pagy.series_nav(:bulma, **options) %>

==- :icon-pin:  Examples

text
/* Detailed source-code truncated for AI context efficiency. */

{{ include "snippets/nav-styles" }}

==- :icon-sliders:  Options

{{ include "options/series" }}

{{ include "options/navs" }}

{{ include "options/helper" }}

===


Toolbox Helpers Series Nav Js (`docs/toolbox/helpers/series_nav_js.md`)


label: series_nav_js icon: code order: 160 image: ""

:icon-code:  series_nav_js{{ include "snippets/js-required" }}

+++Pagy

:::raised
{width=428}
:::
:::raised
{width=358}
:::
:::raised
{width=288}
:::

+++Bootstrap

:::raised
{width=428}
:::
:::raised
{width=358}
:::
:::raised
{width=288}
:::

+++Bulma

:::raised
{width=428}
:::
:::raised
{width=358}
:::
:::raised
{width=288}
:::

+++

:::content-center
{{ include "snippets/run-app" app: "demo" }}
:::

series_nav_js functions similarly to a series_nav, with the following added features:

  1. Optional responsiveness: dynamically fills the container width.
  2. Improves performance and optimizes resource usage (see Maximize performance).

{{ include "snippets/all-but-keyset" }}

=== :icon-tools:  Usage

erb
<%== @pagy.series_nav_js(**options) %>  <%# default pagy style %>
<%== @pagy.series_nav_js(:bootstrap, **options) %>
<%== @pagy.series_nav_js(:bulma, **options) %>

==- :icon-pin:  Examples

text
/* Detailed source-code truncated for AI context efficiency. */

{{ include "snippets/nav-styles" }}

==- :icon-sliders:  Options

{{ include "options/series" }}

steps: { 0 => 5, 540 => 7, ... }
: Enable responsiveness. Assign a different number of :slots to different tag widths. It overrides the :slots option.

{{ include "options/navs" }}

{{ include "options/helper" }}

==- :icon-log:  In Depth: :steps Option

Notice: when :steps is not set, the series_nav_js behaves almost like a series_nav: just faster.

Set it as a hash, where the keys are integers representing the widths in pixels, and the values are the :slots options to be applied for those widths.

For example:

{ 0 => 5, 540 => 7, 720 => 9 } means that from 0 to 540 pixels width, Pagy will use 5 slots, from 540 to 720 it will use 7 slots, and over 720 it will use 9 slots. (Read more about the :slots option in the Control the pagination bar section.)

!!!warning :steps must contain a 0 width
You can set any number of steps with any arbitrary width/slots. The only requirement is that the :steps hash must always contain the 0 width, or a Pagy::OptionError exception will be raised.
!!!

!!! Notice
The :steps option overrides the :slots option, while the :compact option applies to all the steps.
!!!

Setting the right steps

Setting the :steps can enhance responsiveness and ensure seamless transitions.

Consider these guidelines to achieve optimal results:

  1. Define discrete :steps using width/slots pairs to control the pagination behavior.
  2. Ensure the container's width accommodates all slots for a smooth transition as it resizes.
  3. Synchronize the pagy :steps with your container's discrete width changes, for consistent alignment.
  4. Test responsiveness to confirm that assigned slots fit within the corresponding width for each step.

==- :icon-alert:  Caveats

!!!warning HTML Fallback
If JavaScript is disabled in the client browser, this helper will not render anything. You should implement your own HTML fallback:

erb
<noscript><%== @pagy.series_nav %></noscript>

!!!

!!!warning Window Resizing
The series_nav_js elements are automatically re-rendered on window resize. If another function changes the size without causing a window resize, you need to explicitly re-render:

js
document.getElementById('my-pagy-nav-js').render();

!!!

!!!danger Overriding *_js helpers is not recommended
The *_js helpers are tightly coupled with the JavaScript code, so any partial overriding on one side would be quite fragile and might break in future releases.
!!!

===


Toolbox Helpers Urls Hash (`docs/toolbox/helpers/urls_hash.md`)


label: urls_hash icon: list-unordered order: 180

:icon-list-unordered:  urls_hash


urls_hash returns the :first, :previous, :next, :last non-nil URLs hash.

!!! JSON:API
With the jsonapi: true option, the :previous key becomes :prev, as the JSON:API specification requires.
!!!

!!!success It works with all paginators
!!!

=== :icon-tools:  Usage

ruby controller
urls_hash = @pagy.urls_hash(**options)

==- :icon-pin:  Examples

text
/* Detailed source-code truncated for AI context efficiency. */

==- :icon-sliders:  Options

{{ include "options/helper-url" }}

===


Toolbox Paginators Calendar (`docs/toolbox/paginators/calendar.md`)


label: :calendar icon: calendar-24 order: 60 image: ""

:icon-calendar:  :calendar

:::raised
{width=600}
:::

:::content-center
{{ include "snippets/run-app" app: "calendar" }}
:::

!!!warning ActiveSupport Required!
!!!

The :calendar is a TIME-RANGE paginator.

It enables cascade-filtering of the collection by time units (year, quarter, month, week, and day), followed by paginating the filtered collection using the :offset paginator.

!!!success Use this paginator when most pages contain results.
!!!

!!!warning Avoid using it for sparse datasets with numerous empty pages.
!!!

==- :icon-list-ordered:  Setup

You must define a few simple methods in your app to configure and coordinate the objects created by the pagy method. See the following examples and comments (adapted from the Calendar app code).

ruby controller
# Note: All time values must be instances of ActiveSupport::TimeWithZone.
# REQUIRED: return the start and end limits of the collection as a 2-item array
def pagy_calendar_period(collection)
  starting = collection.minimum(:created_at)
  ending   = collection.maximum(:created_at)
  [starting.in_time_zone, ending.in_time_zone]
end

# REQUIRED: return the collection filtered by a time period
def pagy_calendar_filter(collection, from, to)
  collection.where(created_at: from...to)
end

# OPTIONAL: return the array of counts per time unit
# If this method is defined, pagy will add an extra 'empty-page' CSS class
# to the links leading to empty pages, along with a title attribute containing information about each page link.
def pagy_calendar_counts(collection, unit, from, to)
  # If collection is in order: :desc, add the reverse: true option to the next line
  collection.group_by_period(unit, :created_at, range: from...to).count.values
end

# Example usage with default options.
def index
  @calendar, @pagy, @records = pagy(:calendar, collection,
                                    year:  {},
                                    month: {},
                                    offset: {})
end
erb view (template)
<%== @calendar[:year].series_nav %>
<%== @calendar[:month].series_nav %>


<%== @pagy.info_tag %> for <%== @calendar[:month].page_label(@pagy.page, format: '%B %Y') %>.

... Render @records here ...


<%== @pagy.series_nav %>

<p>Showtime: <%= @calendar.showtime %></p>
<a href="<%= @calendar.url_at(Time.zone.now) %>">Go to now</a>

=== :icon-tools:  Usage

ruby
@calendar, @pagy, @records = pagy(:calendar, collection, year: {}, month: {}, offset: {})
  • @calendar is a specialized hash that contains the pagy time unit objects (e.g., :year and :month in this example).
  • @pagy is the object representing the current page of records.
  • @records is the paginated subset of the collection.
  • The :year and :month parameters create time unit objects (default options are used in this example).
  • :offset is the offset instance that paginates the time-filtered collection.

==- :icon-gear:  Configuration

The calendar configuration defines the calendar objects to be generated. These objects filter the collection by the selected time units.

You can include one or more unit levels using keys like :year, :quarter, :month, :week, or :day. Assign each key a hash of unit options. Use an empty hash for default values, e.g., year: {}, month: {}, ....

Unit Options

order: :desc
: Sets the order of the unit, either :asc or :desc. Make sure to order your collection accordingly.

format: '<strftime>'
: Change the label format for the unit links.

!!!warning Option Restrictions
Do not try to set :page, :page_key, :querify, or :period options manually. These options are handled automatically, so setting them explicitly has no effect.

The Pagy::OPTIONS are not applied to the unit objects.
!!!

The `disabled` flag

The calendar is enabled by default. However, you can include an optional :disabled boolean flag in the configuration hash to disable the calendar bars. This is useful to display the regular pages of the collection without any filtering or calendar UI.

Offset Object

  • The :offset paginator object is always used, regardless of the :disabled flag value. If omitted, a default one will be created.
  • It is not subject to the Option Restrictions mentioned above.

==- :icon-hash:  Methods

@calendar.url_at(time, **options)
: Returns a URL complete with all parameters for the pages in each filter bar that includes the given time. For example:
@calendar.url_at(Time.zone.now) generates the filter bar URLs pointing to today.

If time is outside the pagination range, it raises a Pagy::RangeError; however, you can pass the fit_time: true option to avoid the error and get the URL to the page closest to the passed time argument (first or last page).

@calendar.showtime
: Displays the time of the smallest time unit currently visible on the calendar.

==- :icon-location:  Localization

Pagy provides the calendar localization only for en locales. For other locales, pagy requires the rails-i18n gem.

Pass the locales that your app uses to the method localize_with_rails_i18n_gem in the pagy.rb initializer:

ruby
Pagy::Calendar.localize_with_rails_i18n_gem(*your_locales)

==- :icon-alert:  Caveats

!!!warning Calendar pages with no records are visible and accessible.
You may want to display a message when @records.empty?.
!!!

===


Toolbox Paginators Countish (`docs/toolbox/paginators/countish.md`)


label: :countish icon: move-to-end-24 order: 95

:icon-move-to-end:  :countish


:countish is an OFFSET paginator that memoizes the result of the COUNT query in the page links, running it only once per collection (instead of once per page), optionally recounting it when it's stale.

It fully supports all the helpers and navigators.

=== :icon-tools:  Usage

ruby controller
# count only once and memoize for all pages
@pagy, @records = pagy(:countish, collection, **options)
# count once, memoize, and recount when the memoized count is older than ttl
@pagy, @records = pagy(:countish, collection, ttl: 300, **options)
  • @pagy is the pagination instance. It provides the readers and the helpers to use in your code.
  • @records represents the paginated collection of records for the page (lazy-loaded records).
  • :ttl is the Time To Live of the memoized count. Counts only once if nil.

==- :icon-sliders:  Options

ttl: 180
: A positive number of seconds enables recounting. Setting it to nil (falsey) queries the DB for the COUNT only once, and reuses it for all the other pages served.

!!!warning Recounting/TTL

  • Recounting gets the user more precise info and minimizes the page differences with lengthy page-browsing and abundant DB insertions/deletions.
  • It does not fix the OFFSET-intrinsic data-shift of records on active DB insertions/deletions.
    !!!

{{ include "options/offset" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

===


Toolbox Paginators Countless (`docs/toolbox/paginators/countless.md`)


label: :countless icon: move-to-end-24 order: 90

:icon-move-to-end:  :countless


:countless is an OFFSET paginator that avoids the COUNT query, cutting the number of queries per page in half. UI helpers work with some limitations (see Caveat).

!!!warning Consider using the :keynav_js paginator when possible!
The :keynav_js offers identical UI features but uses the faster KEYSET pagination.
!!!

=== :icon-tools:  Usage

ruby controller
@pagy, @records = pagy(:countless, collection, **options)
  • @pagy is the pagination instance. It provides the readers and the helpers to use in your code.
  • @records represents the eager-loaded Array of records for the page.

==- :icon-sliders:  Options

headless: true
: Use this option when UI is unnecessary (e.g., for infinite scrolling) and/or to skip eager loading. In this scenario:

  • @pagy is incompatible with any helpers.
  • @records behaves like a standard collection.
  • The collection ends when @records.size < @pagy.limit.

{{ include "options/offset" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

==- :icon-alert:  Caveat

!!!warning Nav bar links beyond the highest visited page are not known/displayed.
!!!

===


Toolbox Paginators Elasticsearch Rails (`docs/toolbox/paginators/elasticsearch_rails.md`)


label: :elasticsearch_rails icon: search order: 50

:icon-search:  :elasticsearch_rails


:elasticsearch_rails is a SEARCH paginator for ElasticsearchRails search results.

=== :icon-tools:  Usage

+++ Active mode

!!!success Pagy searches and paginates
You use the pagy_search method in place of the search method.
!!!

ruby model
extend Pagy::Search
ruby controller
# Get the collection in one of the following ways
search = Article.pagy_search(params[:q])
search = Article.pagy_search(params[:q]).records
search = Article.pagy_search(params[:q]).results
# Paginate it
@pagy, @response = pagy(:elasticsearch_rails, search, **options)

# IMPORTANT: If the Elasticsearch max_result_window is != 10_000, sync it with pagy
@pagy, @response = pagy(:elasticsearch_rails, search, max_result_window: 1_000, ...)

+++ Passive mode

!!!success You search and paginate
Pagy creates its object out of your result.
!!!

ruby controller
# Standard response (already paginated)
@response = Article.search(params[:q], from: 0, size: 10, ...)
# Get the pagy object out of it
@pagy = pagy(:elasticsearch_rails, @response, **options)

# IMPORTANT: If the Elasticsearch max_result_window is != 10_000, sync it with pagy
@pagy = pagy(:elasticsearch_rails, @response, max_result_window: 1_000, ...)

+++

!!!
Search paginators use the same positional technique as :offset paginators, with shared options and readers.
!!!

==- :icon-sliders:  Options

max_result_window: 1_000
: Set it to the actual max_result_window applied by Elasticsearch, to get an accurate pagination count (default 10_000).

search_method: :my_search
: Customize the name of the ElasticsearchRails search method to use in active mode (default :search).

{{ include "options/paginator" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

===


Toolbox Paginators Keynav Js (`docs/toolbox/paginators/keynav_js.md`)


label: :keynav_js icon: key order: 70

:icon-key:  :keynav_js{{ include "snippets/js-required" }}


:keynav_js is a fast KEYSET paginator that supports the UI. It's a Pagy-exclusive technique.

The Keynav pagination adds the numeric variables (@page, @last, @previous, @next, @in) to its instances, supporting their usage with the UI. It does so by transparently exchanging data with the client, which stores the state of the pagination.

If something goes wrong on the client side, it falls back to the :countless paginator seamlessly.

{{ include "snippets/run-app" app: "keynav" anchor: "keysets" }}

!!!warning This documentation extends the :keyset documentation.
It's easier to understand if you are familiar with the :keyset docs.
!!!

==- :icon-list-ordered:  Setup

Keyset Setup

JavaScript Support

=== :icon-tools:  Usage

ruby controller
@pagy, @records = pagy(:keynav_js, collection, **options)
  • @pagy is the pagination instance. It provides the readers and the helpers to use in your code.
  • @records is the eager-loaded Array of the page records.

==- :icon-sliders:  Options

{{ include "options/keyset" }}

==- :icon-mention:  Readers

last
: The last page.

pages
: The number of pages.

previous
: The previous page

{{ include "snippets/keyset-readers" }}

==- :icon-log:  In Depth: Cutoffs Filtering

Let's take a new look at the diagram of the keyset pagination explained in the Keyset documentation:

text
│ first page (10)  >│ second page (10) >│ last page (9)  >│
beginning of set >[· · · · · · · · · X]· · · · · · · · · Y]· · · · · · · · ·]< end of set
                                      ▲                   ▲
                                   cutoff-X            cutoff-Y

Let's suppose that we navigate to page #3 (i.e., the last page), and we click on the link for page #2. We have stored the cutoff-X, so we can pull the 10 records after cutoff-X again as we did the first time... but are we sure that we would get the same results?

Let's suppose that the database just changed: 1 record was inserted before cutoff-X, and 2 records were deleted after cutoff-X...

text
│ page #1 (11)       >│ page #2 (8)  >│ page #3 (9)    >│
beginning of set >[· · · · · · · · · · X]· · · · · · · Y]· · · · · · · · ·]< end of set
                                        ▲               ▲
                                   cutoff-X        cutoff-Y

At this point, pulling 10 records from the cutoff-X would also get the first 2 records of page #3, and if you then navigate to page #3, you would pull the same 2 records again.

Indeed, not only have the results changed, but the cutoffs appear to have also shifted their absolute position in the set. In reality, the cutoffs have the same value as before, so they maintained their relative position in the set. However, now there is a different number of records falling into the same pages, which is totally consistent with the changes, but possibly unexpected. That is... if you have the mindset of OFFSET pagination, where the pages are split by number of records (absolute position) and not by their position relative to the records in the set.

!!!
The main goal of pagination is to split the results into manageable chunks and ensure it is as fast and accurate as possible, so the variation in the page size doesn't seem relevant to that. However, should it be relevant to you, you can always use the classic OFFSET pagination and accept its slowness and inaccuracy.
!!!

Pagy keynav doesn't use the LIMIT to pull the records of already visited pages. Instead, it replaces the LIMIT with the same filter used for the beginning of the page, and compounds it with the negated filter of the ending of the page.

For example, the filtering of the page could be logically described like:

text
- Page #1 `WHERE NOT AFTER CUTOFF-X`                    <- only ending filter
- Page #2 `WHERE AFTER CUTOFF-X AND NOT AFTER CUTOFF-Y` <- combined beginning + ending filter
- Page #3 `WHERE AFTER CUTOFF-Y LIMIT 10`               <- regular beginning filter (no cutoff for last page)

!!! Implementing page-rebalancing
When the number of records on a visited page has drastically changed, it would be helpful to mitigate the surprise effect on the user by:

  • Automatically compacting the empty (or almost empty) visited pages.
  • Automatically splitting the excessively grown visited pages.
    !!!

==- :icon-alert:  Caveat

!!!warning Nav bar links beyond the highest visited page are not known/displayed.
!!!

===


Toolbox Paginators Keyset (`docs/toolbox/paginators/keyset.md`)


label: :keyset icon: key order: 80

:icon-key:  :keyset


:keyset is the fastest KEYSET paginator for ActiveRecord::Relation or Sequel::Dataset SQL ordered collections.

It uses a much faster pagination technique than OFFSET.

{{ include "snippets/run-app" app: "keyset" anchor: "keysets" }}

==- :icon-blocked:  Constraints

!!!tip When a UI is needed, and using KEYSET Pagination is possible, use the :keynav_js paginator to overcome almost all these constraints.
!!!

!!!warning With any KEYSET pagination technique...

  • You can only paginate from one page to the next: no jumping to arbitrary pages.
  • The set must be uniquely ordered. Add the primary key (usually :id) as the last order column to be sure.
  • You should add the most suitable DB index for your ordering strategy, or there will be no performance gain.
    !!!

!!!warning With Pagy :keyset...
You don't know the previous and the last page; you only know the first and next pages.

:icon-light-bulb: If you want to paginate backward, just call reverse_order on your set, and proceed forward.
!!!

==- :icon-list-ordered:  Setup

Depending on your order requirements, here is how you set it up for maximum efficiency:

+++ No Order Requirement

Just order by :id. It's fast out of the box without any setup...

rb
set = collection.order(:id)

+++ Specific Order Requirement

Ensure that your set is uniquely ordered

{{ include "snippets/mini-step" step: "Option 1" }} If the combination of the values of the ordered columns is certainly unique...

rb
set = collection.order(:brand).order(:model)

{{ include "snippets/mini-step" step: "Option 2" }} If it's possibly not unique, just append a unique column to the order (e.g., your primary keys).

rb
set = collection.order(:last_name).order(:first_name).order(:id)

Ensure that your table has the appropriate index AND index type

{{ include "snippets/mini-step" step: "1" }} The index must include the exact same columns and ordering direction of your set.
{{ include "snippets/mini-step" step: "2" }} The index type must minimize the data scan (e.g., B-tree, B+ tree, ...).

+++

=== :icon-tools:  Usage

ruby controller
@pagy, @records = pagy(:keyset, set, **options)
  • @pagy is the pagination instance. It provides the readers and the helpers to use in your code.
  • @records is the eager-loaded Array of the page records.
erb
<%== @pagy.next_tag(text: 'Next page &gt;') %>

==- :icon-sliders:  Options

{{ include "options/keyset" }}

==- :icon-mention:  Readers

{{ include "snippets/keyset-readers" }}

==- :icon-book:  Glossary

There are a few peculiar aspects of the keyset pagination technique that you might not be familiar with. Here is a concise list:

offset pagination
: The technique to fetch each page by incrementing the offset from the collection start.It requires two queries per page (or only one if you use countless): it's slow toward the end of big tables.It can be used for a rich frontend: it's the classic pagy pagination.

keyset pagination
: The technique to fetch the next page starting after the latest fetched record in a uniquely ordered collection.It requires only one query per page (without OFFSET). If properly indexed, it's the fastest technique, regardless of the table size and position. Supports only infinite pagination, with no other frontend helpers.

keynav pagination
: The Pagy-exclusive technique to use keyset pagination, providing nearly complete UI support. The fastest technique with UI capabilities.

uniquely ordered
: The property of a set, when the concatenation of the values of the ordered columns is unique for each record. It is similar to a composite primary key for the ordered table, but dynamically based on the keyset columns.

set
: The uniquely ordered ActiveRecord::Relation or Sequel::Dataset collection to paginate.

keyset
: The hash of column/order pairs. Pagy extracts it from the order of the set.

keyset attributes
: The hash of attributes of the keyset columns of a record.

cutoff
: The value that identifies where the page ends, and the next one begins. It is the Base64 URL-safe encoded string of the serialized array of the keyset attribute values.

cutoffs
: The array of cutoffs of the known pagination state, used only by keynav to keep track of the visited pages during the navigation. They are cached in the sessionStorage of the client.

page
: The current page, i.e. the page of records beginning after the cutoff of the previous page. Also the :page option, which is set to the cutoff of the previous page.

next
: The next page, i.e. the page of records beginning after the cutoff. Also the cutoff value returned by the next method.

==- :icon-log:  In Depth: Cutoffs

The cutoff of a page is the value that identifies where the page has ended, and the next one begins.

Let's consider an example of a simple set of 29 records, with an id column populated by character keys, and its order is: order(:id).

Assuming a LIMIT of 10, the "first page" will just include the first 10 records in the set: no cutoff value is known so far...

text
│ first page (10)  >│ rest (19)                          >│
beginning of set >[· · · · · · · · · ·]· · · · · · · · · · · · · · · · · · ·]< end of set

At this point, it's the exact same first page pulled with OFFSET pagination, however, we don't want to use OFFSET to get the records after the first 10: that would be slow in a big table, so we need a way to identify the beginning of the next page without COUNTing the records (i.e., the whole point we want to avoid for performance).

So we read the id of the last one, which is the value X in our example... and that is the cutoff value of the first page. It can be described as: "the point up to the value X in the id column".

Notice that this is not like saying "up to the record X". It's important to understand that a cutoff refers just to a cutoff value of a column (or the values of multiple columns, in case of multi-column keysets).

Indeed, that very record could be deleted right after we read it, and our cutoff-X will still be the valid truth that we paginated the set up to the "X" value, cutting any further record off the page...

text
│ first page (10)  >│ second page (10) >│ rest (9)       >│
beginning of set >[· · · · · · · · · X]· · · · · · · · · ·]· · · · · · · · ·]< end of set
                                      ▲
                                   cutoff-X

For getting the next page of records (i.e. the "second page") we pull the next 10 records AFTER the cutoff-X. Again, we read the id of the last one, which is Y: so we have our new cutoff-Y, which is the end of the current page, and the next will go AFTER it...

text
│ first page (10)  >│ second page (10) >│ last page (9)  >│
beginning of set >[· · · · · · · · · X]· · · · · · · · · Y]· · · · · · · · ·]< end of set
                                      ▲                   ▲
                                   cutoff-X            cutoff-Y

When we pull the next page from the cutoff-Y, we find only the remaining 9 records, which means that it's the "last page", which naturally ends with the end of the set, so it doesn't have any cutoff value to separate it from further records.

!!! Keynotes

  • A cutoff identifies a "cutoff value" for a page in the set. It is not a record, nor a reference to it.
  • Its value is extracted from the keyset attributes values array of the last record of the page, converted to JSON, and encoded as a Base64 URL-safe string, for easy use in URLs.
    • The :keyset paginator embeds it in the request URL; the :keynav_js paginator caches it on the client sessionStorage.
  • All the pages but the last end with the cutoff.
  • All the pages but the first begin AFTER the cutoff of the previous page.
    !!!

==- :icon-stop:  Troubleshooting

:::
==- {{ include "snippets/mini-step" step:"1" }} Records may repeat or be missing from successive pages

||| :icon-question: Order issue

Not unique combination...

rb
Product.order(:name, :production_date)

||| :icon-check-circle: Append the primary key to the order

The :id is usually the primary key...

rb
Product.order(:name, :production_date, :id)

|||

||| :icon-question: Encoding issue

The generic to_json method used to encode the page may lose some information when decoded.

||| :icon-check-circle: Solution

  • Check the actual executed DB query and the actual stored value.
  • Identify the column that has a format that doesn't match the keyset.
  • Override the encoding with the :pre_serialize option.

|||

==- {{ include "snippets/mini-step" step:"2" }} The order is OK, but the DB is still slow

||| :icon-question: Index issue

The index has the wrong order, or it's the wrong type.

||| :icon-check-circle: Solutions

  • Ensure that the index reflects exactly the column sequence and order of your keyset.
  • Use a B-tree or B+ Tree index. Use SQL EXPLAIN ANALYZE or a similar tool to confirm no table scan is performed.
  • With same-direction keysets, enabling the :tuple_comparison option may help if your DB supports it.

|||

===


Toolbox Paginators Meilisearch (`docs/toolbox/paginators/meilisearch.md`)


label: :meilisearch icon: search order: 40

:icon-search:  :meilisearch


:meilisearch is a SEARCH paginator for Meilisearch search results.

=== :icon-tools:  Usage

+++ Active mode

!!!success Pagy searches and paginates
You use the pagy_search method in place of the ms_search method.
!!!

ruby model
extend Pagy::Search
ActiveRecord_Relation.include Pagy::Search # Statically enable pagy_search on the model relations
ruby controller
# Get the collection in one of the following ways
search = Article.pagy_search(params[:q])
search = Article.pagy_search(params[:q]).results
# Paginate it
@pagy, @response = pagy(:meilisearch, search, **options)

+++ Passive mode

!!!success You search and paginate
Pagy creates its object out of your result.
!!!

ruby controller
# Standard results (already paginated)
@results = Article.ms_search(params[:q], hits_per_page: 10, page: 10, ...)
# Get the pagy object out of it
@pagy    = pagy(:meilisearch, @results, **options)

+++

!!!
Search paginators use the same positional technique as :offset paginators, with shared options and readers.
!!!

==- :icon-sliders:  Options

search_method: :my_search
: Customize the name of the Meilisearch search method to use in active mode (default :ms_search).

{{ include "options/paginator" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

===


Toolbox Paginators Offset (`docs/toolbox/paginators/offset.md`)


label: :offset icon: move-to-end order: 100

:icon-move-to-end:  :offset


:offset is a generic OFFSET paginator usable with ORM collections or regular Array objects.

It uses the complete OFFSET pagination technique, which triggers two SQL queries per request:

  • a COUNT query to get the count;
  • an OFFSET + LIMIT query to get the records.

It fully supports all the helpers and navigators.

!!!warning Consider using the :countish paginator when possible!
The :countish paginator offers identical UI features, but it's up to 2x faster.
!!!

=== :icon-tools:  Usage

ruby controller
@pagy, @records = pagy(:offset, collection, **options)
  • @pagy is the pagination instance. It provides the readers and the helpers to use in your code.
  • @records represents the paginated collection of records for the page (lazy-loaded records).

==- :icon-sliders:  Options

{{ include "options/offset" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

===


Toolbox Paginators Rails Active Search (`docs/toolbox/paginators/rails_active_search.md`)


label: :rails_active_search
icon: search
order: 35
nav:
badge:
text: "NEW"
variant: info



:rails_active_search is a SEARCH paginator for ActiveSearch (rails-active_search gem) search results.

{{ include "snippets/run-app" app: "active_search" anchor: "active-search" }}

=== :icon-tools:  Usage

+++ Active mode

!!!success Pagy searches and paginates
You use the pagy_search method in place of the search method.
!!!

ruby model
extend Pagy::Search
ruby controller
# Get the collection (ActiveSearch keyword arguments, e.g., index: or scope:, are passed through)
search = Article.pagy_search(params[:q])
# Chain any ActiveSearch::Query method that returns a query (e.g., filter, sort, highlight)
search = Article.pagy_search(params[:q]).filter(status: 'published').sort(published_at: :desc)
# Paginate it: @results is the ActiveSearch::Results of the page
@pagy, @results = pagy(:rails_active_search, search, **options)

!!!warning
Do not chain the pagination methods (limit, offset, page) nor the methods that end the chain (e.g., results, to_native_query).
!!!

+++ Passive mode

!!!success You search and paginate
Pagy creates its object out of your result.
!!!

ruby controller
# Standard results (already paginated)
@results = Article.search(params[:q]).limit(10).offset(20).results
# or
@results = Article.search(params[:q]).page(3, per_page: 10).results
# Get the pagy object out of it
@pagy    = pagy(:rails_active_search, @results, **options)

!!!warning
The :limit and :page are extracted from the results, so use a single Integer per_page with page: an Array of page sizes is not supported.
!!!

+++

!!!
Search paginators use the same positional technique as :offset paginators, with shared options and readers.
!!!

==- :icon-sliders:  Options

search_method: :my_search
: Customize the name of the ActiveSearch search method to use in active mode (default :search).

{{ include "options/paginator" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

===


Toolbox Paginators Searchkick (`docs/toolbox/paginators/searchkick.md`)


label: :searchkick icon: search order: 30

:icon-search:  :searchkick


:searchkick is a SEARCH paginator for Searchkick search results.

=== :icon-list-ordered:  Setup

ruby pagy.rb (initializer)
Searchkick.extend Pagy::Search

=== :icon-tools:  Usage

+++ Active mode

!!!success Pagy searches and paginates
You use the pagy_search method in place of the search method.
!!!

ruby model
extend Pagy::Search
ruby controller
# Single model
search = Article.pagy_search(params[:q])
# Multiple models
search = Searchkick.pagy_search(params[:q], models: [Article, Category])
# Paginate it
@pagy, @response = pagy(:searchkick, search, **options)

# IMPORTANT: If the Elasticsearch max_result_window is != 10_000, sync it with pagy
@pagy, @response = pagy(:searchkick, search, max_result_window: 1_000, ...)

+++ Passive mode

!!!success You search and paginate
Pagy creates its object out of your result.
!!!

ruby controller
# Standard results (already paginated)
@results = Article.search(params[:q], page: 1, per_page: 10, ...)
# Get the pagy object out of it
@pagy = pagy(:searchkick, @results, **options)

# IMPORTANT: If the Elasticsearch max_result_window is != 10_000, sync it with pagy
@pagy = pagy(:searchkick, @results, max_result_window: 1_000, ...)

+++

!!!
Search paginators use the same positional technique as :offset paginators, with shared options and readers.
!!!

==- :icon-sliders:  Options

max_result_window: 1_000
: Set it to the actual max_result_window applied by Elasticsearch, to get an accurate pagination count (default 10_000).

search_method: :my_search
: Customize the name of the Searchkick search method to use in active mode (default :search).

{{ include "options/paginator" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

===


Toolbox Paginators Typesense Rails (`docs/toolbox/paginators/typesense_rails.md`)


label: :typesense_rails icon: search order: 20

:icon-search:  :typesense_rails


:typesense_rails is a SEARCH paginator for Typesense::Rails search results.

=== :icon-list-ordered:  Setup

Ensure Typesense.configuration[:pagination_backend] == nil.

=== :icon-tools:  Usage

+++ Active mode

!!!success Pagy searches and paginates
You use the pagy_search method in place of the search method.
!!!

ruby model
extend Pagy::Search
ruby controller
# Get the collection
search = Article.pagy_search(params[:q], to_query)
# Paginate it
@pagy, @response = pagy(:typesense_rails, search, **options)

+++ Passive mode

!!!success You search and paginate
Pagy creates its object out of your result.
!!!

ruby controller
# Standard results (already paginated)
@results = Article.search(params[:q], to_query, { per_page: 10, page: 10, ... })
# Get the pagy object out of it
@pagy    = pagy(:typesense_rails, @results, **options)

+++

!!!
Search paginators use the same positional technique as :offset paginators, with shared options and readers.
!!!

==- :icon-sliders:  Options

search_method: :my_search
: Customize the name of the Typesense::Rails search method to use in active mode (default :search).

{{ include "options/paginator" }}

==- :icon-mention:  Readers

{{ include "snippets/offset-readers" }}

===


--- METRICS ---