| name | create-filter |
| description | Use when adding a filter to a Backpex LiveResource, creating a filter module, or the user asks about filtering data in Backpex index views. |
Creating Backpex Filters
When the user wants to add a filter:
- Pick the right filter type from the quick reference
- Generate the filter module
- Wire it into the LiveResource's
filters/0 callback
Quick Reference
| Type | Use for | use module | Key callbacks |
|---|
| Boolean | Checkbox predicates (published yes/no) | Backpex.Filters.Boolean | options/1 returning %{label, key, predicate} maps |
| Select | Single-value dropdown | Backpex.Filters.Select | prompt/0, options/1 returning {label, value} tuples |
| MultiSelect | Multi-value dropdown with checkboxes | Backpex.Filters.MultiSelect | prompt/0, options/1 returning {label, value} tuples |
| Range | Date, datetime, or number ranges | Backpex.Filters.Range | type/0 returning :date, :datetime, or :number |
| Custom | Anything else | Backpex.Filter | query/4, render/1, render_form/1 |
All built-in filters auto-implement query/4, render/1, and render_form/1. Do NOT re-implement those unless you need custom behavior.
All filters have these overridable callbacks with defaults:
label/0: filter label (optional callback, can also be set in filters/0 map)
can?/1: visibility control, receives assigns, default true
type/1: Ecto type for validation (default :string), receives assigns
changeset/3: custom changeset validation (default: no-op)
validate/2: public validation API, builds changeset from type/1 and changeset/3
Boolean Filter
Multiple options selectable via checkboxes, combined with OR. Predicates use Ecto.Query.dynamic/2.
defmodule MyAppWeb.Filters.PostPublished do
use Backpex.Filters.Boolean
import Ecto.Query
@impl Backpex.Filter
def label, do: "Published?"
@impl Backpex.Filters.Boolean
def options(_assigns) do
[
%{label: "Published", key: "published", predicate: dynamic([x], x.published)},
%{label: "Not published", key: "not_published", predicate: dynamic([x], not x.published)}
]
end
end
Select Filter
Single-value dropdown. Default query/4 does WHERE field = value.
defmodule MyAppWeb.Filters.PostCategorySelect do
use Backpex.Filters.Select
import Ecto.Query
alias MyApp.Repo
@impl Backpex.Filter
def label, do: "Category"
@impl Backpex.Filters.Select
def prompt, do: "Select category ..."
@impl Backpex.Filters.Select
def options(_assigns) do
from(c in MyApp.Category, select: {c.name, c.id}, order_by: c.name) |> Repo.all()
end
end
MultiSelect Filter
Same as Select but allows multiple values. Default query/4 does WHERE field IN values. Uses prompt/0 (implement with @impl Backpex.Filters.Select) and options/1 (implement with @impl Backpex.Filters.MultiSelect). Note: MultiSelect internally sets @behaviour Backpex.Filters.Select for the prompt callback.
Range Filter
Renders "From" and "To" inputs. Note: type/0 is arity 0, not arity 1.
defmodule MyAppWeb.Filters.PostLikeRange do
use Backpex.Filters.Range
@impl Backpex.Filters.Range
def type, do: :number
@impl Backpex.Filter
def label, do: "Likes"
end
For dates use def type, do: :date. For datetimes use def type, do: :datetime.
Custom Filter
Use Backpex.Filter directly when no built-in type fits. You must implement query/4, render/1, and render_form/1. Note: use Backpex.Filter does not import HEEx sigils. You need use Phoenix.Component for ~H support.
defmodule MyAppWeb.Filters.PostCustom do
use Phoenix.Component
use Backpex.Filter
import Ecto.Query
@impl Backpex.Filter
def label, do: "Custom"
@impl Backpex.Filter
def query(query, attribute, value, _assigns) do
where(query, [x], field(x, ^attribute) == ^value)
end
@impl Backpex.Filter
def render(assigns) do
~H"{@value}"
end
@impl Backpex.Filter
def render_form(assigns) do
~H"""
<input type="text" name={@form[@field].name} value={@value} class="input input-sm" />
"""
end
end
Wiring Into a LiveResource
filters/0 returns a keyword list. Each key is the schema field atom being filtered.
@impl Backpex.LiveResource
def filters do
[
published: %{
module: MyAppWeb.Filters.PostPublished
},
category_id: %{
module: MyAppWeb.Filters.PostCategorySelect,
label: "Category"
},
likes: %{
module: MyAppWeb.Filters.PostLikeRange,
label: "Likes",
presets: [
%{label: "Over 100", values: fn -> %{"start" => 100, "end" => nil} end},
%{label: "1-99", values: fn -> %{"start" => 1, "end" => 99} end}
]
}
]
end
Filter map keys
| Key | Required | Description |
|---|
:module | yes | The filter module |
:label | no | Overrides the module's label/0 |
:default | no | Pre-selected value on initial page load |
:presets | no | Quick-select shortcuts: [%{label: String.t(), values: (-> value)}] |
Conventions
- Module naming:
MyAppWeb.Filters.<Resource><FilterName> (e.g. MyAppWeb.Filters.PostPublished)
- File location:
lib/my_app_web/filters/<snake_case_name>.ex
- The keyword list key in
filters/0 must match the database column or foreign key
- Always
import Ecto.Query when using dynamic/2 or writing custom queries
- Database queries in
options/1 are fine since it runs at render time