Compared to the Rails where there are plenty easy to set up and use pagination gems (Kaminary, will_paginate to cite just some of them), it looks a bit surprising to not find a lot of similar libraries for Phoenix apps.
I took a look at existing ones in Elixir Toolbox, and tried to implement 2 of them:
Paginator was last updated in October 2022 and simply failed to install when running mix reps.get because of its lower dependency version of plug_crypto.
As for Flop Phoenix, I followed the installation and set up instructions for Flop, refactor the simple function listing companies as suggested:
def list_pets(params) do
Flop.validate_and_run!(Pet, params, for: Pet, replace_invalid_params: true)
end
but it also failed with the stack traces pointing to their library code.
I wonder what is the actual way of doing this kind of stuff, what is the preferred library to use or may be in most cases we have to implement the pagination in its own way?
[error] ** (MatchError) no match of right hand side value: {[%Jobex.Sources.Company{__meta__: #Ecto.Schema.Metadata<:loaded, "companies">, id: "05d333c5-d248-44c3-bd36-d21beb7f064a", name: "omise.co", country: "remote", positions: #Ecto.Association.NotLoaded<association :positions is not loaded>, contacts: #Ecto.Association.NotLoaded<association :contacts is not loaded>, inserted_at: ~U[2025-05-03 17:03:01Z], updated_at: ~U[2025-05-03 17:03:01Z]},
...
Once you change handle_params/3 to match just to a tuple without :ok → {pets, meta}, it works.
Here is the complete versions of the live view module:
defmodule JobexWeb.CompanyLive.Index do
use JobexWeb, :live_view
alias Jobex.Sources
@impl true
def handle_params(params, _, socket) do
# {:ok, {companies, meta}} = Sources.list_companies(params)
{companies, meta} = Sources.list_companies(params)
{:noreply, socket |> assign(:meta, meta) |> stream(:companies, companies, reset: true)}
end
...
@impl true
def render(assigns) do
~H"""
<.header>
Listing Companies
</.header>
<Flop.Phoenix.table items={@streams.companies} meta={@meta} path={~p"/companies"}>
<:col :let={{_, company}} label="Name" field={:name}>{company.name}</:col>
<:col :let={{_, company}} label="Country" field={:age}>{company.country}</:col>
</Flop.Phoenix.table>
<Flop.Phoenix.pagination meta={@meta} path={~p"/companies"} />
"""
end
Here is the context module Sources:
def list_companies(params) do
Flop.validate_and_run!(Company, params, for: Company, replace_invalid_params: true)
end
Is there a way to pass tailwind classes to the table and its rows? Otherwise the styling is not so fancy, almost non-existing
Thank you
P.S. For unknown reason, both VS Code and Zed raise the compilation error once I add the dependency to the Mix file. I’m using these versions:
Right, the library doesn’t come with any default styles. You can pass classes with the *_attrs options and attributes. Some via the opts attribute, and some with the attributes on the :col slot.
No need for raised eyebrows—Flop is a very powerful and complete library! Anything I’ve felt I should be able to do has been possible. However, it can definitely feel a little daunting at times and some stuff has a bit of a verbose API, but there are a lot of Flop users around here so certainly feel free to ask questions!
Not to sound like a bootlicker with woylie in the chat but FlopPhoenix+LetMe has been a staple for new (non-Ash) projects to replace Kamari+Pundit (and of course Flop does more than Kamanari).