log1x/pagi
Pagi is a lightweight pagination package for WordPress and Sage themes that brings a Laravel-style API to archive, search, and blog listings. It provides a simple helper and template-friendly rendering for clean, accessible page links in Laravel-based WordPress projects.
log1x/pagi is a thin, opinionated rendering layer over Laravel's own paginator — it does not replace pagination logic, only the markup you'd otherwise hand-roll around $paginator->links(). Day-to-day, that means the upgrade path is boring in the best way: you install it, swap one Blade call, and get consistent pagination output across the app.
Minimal setup:
composer require log1x/pagi
There is nothing to register manually — the service provider is auto-discovered. If the package ships publishable views or config, that's the first thing to check, since the default markup is almost never exactly what your design system wants:
php artisan vendor:publish --provider="Log1x\Pagi\PagiServiceProvider"
First use case: take an existing controller that already returns a paginator (Post::paginate(15)) and replace the Blade call in your index view with pagi's renderer. If your app has three or four listing screens with slightly different pagination markup, converting them all in one pass is where the payoff shows up — it collapses duplicated Tailwind/Bootstrap partials into one call site.
v1.1.1 is purely a dependency bump to permit Laravel 12. Treat it as a compatibility confirmation rather than a feature release.
Standard server-rendered listings. Paginate in the controller, render in the view. Keep the paginate() call in the controller — pagi only owns presentation, so don't try to push business logic into it.
Preserve query strings. Any filtered or sorted listing needs the page links to carry the current query string, otherwise pagination silently drops the user's filters on page 2:
$posts = Post::where('status', 'published')
->paginate(15)
->withQueryString();
Multiple paginators on one page. If two independent listings share a page, give each its own pageName in the paginate() call, or they will both read ?page= and clobber each other.
Wrap it once. Rather than calling the package directly in every view, put it behind your own Blade component (<x-pagination :paginator="$posts" />). That gives you a single place to change markup later and insulates views from any future package API churn.
JSON / API resources. Skip pagi entirely for API responses. Use $paginator->toArray() or an API Resource and let the client render — pagi's value is server-side HTML.
Livewire. Pair it with WithPagination and $this->resetPage() when filters change, otherwise you'll land on an out-of-range page and get an empty table with valid-looking links.
Styling. If your CSS build uses Tailwind's content scanning, verify the package's view paths are picked up (or publish the views into resources/views/vendor/). Class-based purging is the most common reason pagination renders unstyled after install.
laravel/framework constraint resolve cleanly, then move on — no migration or code changes required.?page= handling) is Laravel's LengthAwarePaginator. Debug there with dd($paginator->toArray()) before suspecting the package.simplePaginate and cursorPaginate are different shapes. A renderer written for a LengthAware paginator often assumes lastPage(); simple paginators only expose "next/previous". Test both if you use them.resources/views/vendor/, package updates no longer reach them. Note it in your README so the next developer knows to diff against the vendor copy.Paginator::defaultView() in a service provider, be explicit about which renderer wins — it's easy to end up with mixed Bootstrap and Tailwind output on different screens.cache()->remember("posts.{$page}") or nothing at all; forgetting the page number is a classic self-inflicted bug.composer.lock you actually commit, especially across Laravel majors.How can I help you explore Laravel packages today?