Pagex ๐
Fast, minimal, production-ready pagination for Ecto and Phoenix.
Inspired by Pagy (Ruby), Pagex is designed specifically for the Elixir/Phoenix ecosystem. It takes the approach of explicit, small functions over magic. No use Pagex in your schema, no global config macros, and no hidden schema introspection.
๐ Example Integration
If you want to see a full, production-ready implementation of pagex in a Phoenix application, check out our example repository:
๐ Simple Pagination Example
This example project demonstrates:
- โ Phoenix LiveView pagination.
- โ JSON API pagination.
๐บ Demo Videos
Phoenix LiveView Integration
See how smoothly pagex handles real-time pagination in LiveView:
Click to view LiveView Demo
JSON API Response
Standardized metadata structure ready for any front-end:
Click to view JSON API Demo
โจ Features & Comparison
Pagex offers a modern alternative to existing pagination libraries by combining performance with extreme simplicity.
| Feature | Pagex | Scrivener | Paginator |
|---|---|---|---|
| Offset pagination | โ | โ | โ |
| Cursor pagination | โ | โ | โ |
| LiveView helpers | โ | โ | โ |
| JSON API support | โ | Partial | Partial |
| No macros/DSLs | โ | โ | โ |
| Max page size guard | โ | โ | โ |
| Optional count query | โ | โ | โ |
Pagination Benchmark
Comparing offset-based vs cursor-based pagination โ with and without COUNT(*) โ across different page depths.
Test Environment
| Parameter | Value |
|---|---|
| Page size | (not specified) |
| Dataset size | (not specified) |
| Database | (add here) |
| Measurement | Average latency, p99 latency, memory usage per query |
| Cache | (add warm/cold if relevant) |
Key Findings
-
โ
Fastest overall:
offset pagination (no count) -
โ Worst performance:
offset pagination with COUNT(*)(~45ร slower) - ๐ Cursor pagination: slower per request, but more stable for deeper pagination
- ๐พ Memory tradeoff: cursor chaining increases memory usage significantly (~2ร)
Performance Results
| Method | Throughput (ops/sec) | Avg Latency | Deviation | Median | p99 |
|---|---|---|---|---|---|
| Offset page 1 (no count) | 16.49K | 60.64 ยตs | ยฑ12.79% | 59.67 ยตs | 81.04 ยตs |
| Cursor first page | 15.44K | 64.77 ยตs | ยฑ27.23% | 60.29 ยตs | 146.58 ยตs |
| Offset page 100 (no count) | 12.18K | 82.07 ยตs | ยฑ8.56% | 80.96 ยตs | 100.05 ยตs |
| Cursor next page chain | 8.22K | 121.71 ยตs | ยฑ9.15% | 119.92 ยตs | 160.47 ยตs |
| Offset page 1 (with count) | 0.37K | 2731.43 ยตs | ยฑ5.89% | 2738.56 ยตs | 3195.07 ยตs |
| Offset page 100 (with count) | 0.36K | 2759.19 ยตs | ยฑ5.48% | 2766.46 ยตs | 3148.26 ยตs |
Relative Speed (vs. fastest baseline)
| Method | Relative Speed | Latency Delta |
|---|---|---|
| Offset page 1 (no count) | 1.00ร (baseline) | โ |
| Cursor first page | 1.07ร slower | +4.13 ยตs |
| Offset page 100 (no count) | 1.35ร slower | +21.44 ยตs |
| Cursor next page chain | 2.01ร slower | +61.07 ยตs |
| Offset page 1 (with count) | 45.05ร slower | +2670.79 ยตs |
| Offset page 100 (with count) | 45.50ร slower | +2698.55 ยตs |
Memory Usage
| Method | Avg Memory | Deviation | Median | p99 |
|---|---|---|---|---|
| Offset page 1 (no count) | 55.06 KB | ยฑ0.07% | 55.05 KB | 55.19 KB |
| Cursor first page | 53.68 KB | ยฑ0.02% | 53.67 KB | 53.72 KB |
| Offset page 100 (no count) | 55.30 KB | ยฑ0.02% | 55.30 KB | 55.34 KB |
| Cursor next page chain | 109.01 KB | ยฑ0.01% | 109.02 KB | 109.02 KB |
| Offset page 1 (with count) | 76.89 KB | ยฑ0.27% | 76.83 KB | 77.75 KB |
| Offset page 100 (with count) | 77.10 KB | ยฑ0.16% | 77.09 KB | 77.59 KB |
Relative Memory (vs. baseline)
| Method | Relative Memory | Delta |
|---|---|---|
| Offset page 1 (no count) | 1.00ร (baseline) | โ |
| Cursor first page | 0.97ร | โ1.38 KB |
| Offset page 100 (no count) | 1.00ร | +0.24 KB |
| Cursor next page chain | 1.98ร | +53.95 KB |
| Offset page 1 (with count) | 1.40ร | +21.83 KB |
| Offset page 100 (with count) | 1.40ร | +22.04 KB |
Conclusion
Offset pagination without COUNT(*) performs best for shallow pages and simple use cases, but degrades with expensive counting operations.
Cursor-based pagination provides more predictable scaling for deeper pagination, but introduces higher per-request latency and increased memory usage when chaining queries.
When to use what
| Scenario | Recommendation |
|---|---|
| Simple, shallow listing pages | โ Offset pagination (no count) |
| Deep pagination / large datasets | โ Cursor pagination |
| High-traffic hot paths |
โ Avoid COUNT(*) |
๐ฆ Installation
Add pagex to your list of dependencies in mix.exs:
# mix.exs
def deps do
[
{:pagex, "~> 0.2.3"}
]
endThen, run mix deps.get in your terminal.
๐ Documentation
Full API documentation is available via HexDocs:
To generate and view the full API documentation locally:
mix docsThen open:
open doc/index.htmlOn Linux:
xdg-open doc/index.htmlOr simply open doc/index.html in your browser.
๐ Quick Start
Pagex provides a clean and straightforward API. You can paginate your Ecto queries using either Offset or Cursor-based pagination.
Offset Pagination
Ideal for standard table views and web interfaces.
alias App.Repo
alias App.Blog.Post
# Using default params
{posts, meta} = Pagex.paginate(Post, params, Repo)
# With a custom Ecto query
query = from p in Post, where: p.published == true, order_by: [desc: p.inserted_at]
{posts, meta} = Pagex.paginate(query, %{"page" => 2, "page_size" => 20}, Repo)Cursor Pagination
Highly recommended for large datasets, JSON APIs, and infinite scrolling interfaces.
{posts, meta} = Pagex.paginate_cursor(Post, params, Repo)(Note: The returned meta struct contains helpful data like next_cursor, prev_cursor, total_pages, etc., depending on your chosen pagination strategy.)
๐ป Phoenix & LiveView Integration
Pagex is built with Phoenix in mind. While staying un-intrusive, it ships with out-of-the-box helpers for:
- HTML Views: Easy-to-use template functions to generate pagination links.
- LiveView: Drop-in helpers for managing pagination state and events without boilerplate.
- JSON APIs: Standardized metadata structures ready to be merged into your API responses.
Project Structure
pagex/
โโโ lib/
โ โโโ pagex.ex # Public API โ paginate/4 and paginate_cursor/4
โ โโโ pagex/
โ โโโ meta.ex # Meta struct + constructors
โ โโโ params.ex # Parameter validation
โ โโโ offset.ex # Offset pagination engine
โ โโโ cursor.ex # Cursor pagination engine
โ โโโ phoenix/
โ โโโ live_view.ex # Phoenix LiveView helpers
โ โโโ html.ex # HTML helper functions
โ
โ
โ
โโโ test/
โ โโโ pagex/
โ โโโ params_test.exs
โ โโโ meta_test.exs
โ โโโ cursor_encode_test.exs
โ โโโ phoenix/
โ โโโ html_test.exs
โ โโโ live_view_test.exs
โโโ benchmarks/
โ โโโ support/
โ โโโ post.ex
โ โโโ repo.ex
โ โโโ pagination_benchmark.exs
โ โโโ setup.exs
โโโ mix.exs
โโโ README.md
โโโ CHANGELOG.md
โโโ LICENSE๐ค Contributing
Contributions, bug reports, and feature requests are welcome!
Feel free to check the issues page to get involved.
- Fork and Clone
First, fork the repository by clicking the "Fork" button at the top right of this page [1]. Then, clone your fork to your local machine:
git clone https://github.com/Null-logic-0/pagex.git
cd pagex- Install Dependencies
Pagex is an Elixir project. Fetch the required dependencies using mix:
mix deps.get- Create a Branch
Create a new branch for your feature, improvement, or bug fix:
git checkout -b feature/my-awesome-feature- Make Your Changes
Write your code and implement your changes. If you are adding a new feature or fixing a bug, please write tests to cover your changes to maintain the library's stability.
- Run Tests & Format Code
Before committing, ensure that all tests pass and that the code adheres to the standard Elixir formatting rules:
# Run the test suite
mix test
# Format the code
mix format- Commit and Push
Commit your changes with a descriptive message and push the branch to your fork:
git add .
git commit -m "Add my awesome feature"
git push origin feature/my-awesome-feature- Open a Pull Request
Go back to the main Pagex repository and you'll see a prompt to open a Pull Request. Submit your PR against the master branch and describe the changes you've made!
Why this is helpful:
Elixir specific: It uses the standard
mixcommands (mix deps.get,mix test,mix format) that Elixir developers expect.Step-by-step: It walks beginners completely through the process of interacting with a GitHub repo, making it much more inviting for open-source newcomers.
๐ License
Pagex is open-source software released under the MIT LICENSE