FilterBar component for PhiaUI.
A horizontal toolbar for composing table and list filters. Combines a full-text search input, labelled select dropdowns, checkbox toggles, and a reset button into a cohesive, accessible filter UI.
Each sub-component is independently usable — you can mix and match them in
any combination above a data_grid/1 or table/1.
When to use
Use FilterBar for "simple" filter scenarios where users can select from
predefined options or type a search query. For power users who need to build
complex multi-condition queries (AND/OR, operators), use FilterBuilder
instead.
Anatomy
| Component | Element | Purpose |
|---|---|---|
filter_bar/1 | div | Flex row wrapper for all filter sub-components |
filter_search/1 | input | Full-text search with magnifier icon |
filter_select/1 | select | Labelled native dropdown for enum filters |
filter_toggle/1 | input | Checkbox toggle for boolean filters |
filter_reset/1 | button | Clear all active filters |
Complete example — user management table
<.filter_bar>
<.filter_search
placeholder="Search by name or email…"
on_search="search_users"
value={@search_query}
/>
<.filter_select
label="Role"
name="role"
options={[
{"All roles", ""},
{"Admin", "admin"},
{"Editor", "editor"},
{"Viewer", "viewer"}
]}
value={@filter_role}
on_change="filter_role"
/>
<.filter_select
label="Status"
name="status"
options={[{"All", ""}, {"Active", "active"}, {"Suspended", "suspended"}]}
value={@filter_status}
on_change="filter_status"
/>
<.filter_toggle
label="Show archived"
name="archived"
checked={@show_archived}
on_change="toggle_archived"
/>
<.filter_reset on_click="reset_filters" />
</.filter_bar>LiveView handlers
def handle_event("search_users", %{"value" => q}, socket) do
{:noreply, assign(socket, search_query: q)}
end
def handle_event("filter_role", %{"role" => role}, socket) do
{:noreply, assign(socket, filter_role: role)}
end
def handle_event("filter_status", %{"status" => status}, socket) do
{:noreply, assign(socket, filter_status: status)}
end
def handle_event("toggle_archived", %{"archived" => checked}, socket) do
{:noreply, assign(socket, show_archived: checked == "true")}
end
def handle_event("reset_filters", _params, socket) do
{:noreply, assign(socket,
search_query: "",
filter_role: "",
filter_status: "",
show_archived: false
)}
endDebouncing search
Pair filter_search/1 with phx-debounce on the input to avoid a server
round-trip on every keystroke:
<.filter_search
on_search="search_users"
value={@query}
rest={%{"phx-debounce" => "300"}}
/>
Summary
Functions
Renders the filter bar container.
Renders a reset / clear-all button for the filter bar.
Renders a full-text search input with a magnifier icon.
Renders a labelled native select dropdown.
Renders a checkbox toggle with a visible inline label.
Functions
Renders the filter bar container.
A flex-wrap row that holds all filter sub-components. The flex-wrap means
filters gracefully reflow to a second line on smaller screens.
Typically placed directly above a data_grid/1 or table/1:
<.filter_bar class="mb-4">
<.filter_search ... />
<.filter_select ... />
</.filter_bar>
<.data_grid rows={@filtered_rows} columns={@columns} />Attributes
class(:string) - Additional CSS classes for the root wrapper. Defaults tonil.- Global attributes are accepted. HTML attributes forwarded to the root div.
Slots
inner_block(required) -filter_search/1,filter_select/1,filter_toggle/1, and/orfilter_reset/1children.
Renders a reset / clear-all button for the filter bar.
Fires on_click via phx-click. The handler should reset all filter assigns
to their defaults (empty string, false, nil) and re-apply the query.
Example
<.filter_reset on_click="reset_filters" />
# Or with a custom label
<.filter_reset on_click="clear_all" label="Clear all filters" />
# LiveView handler
def handle_event("reset_filters", _params, socket) do
{:noreply, assign(socket, query: "", status: "", archived: false)}
endAttributes
on_click(:string) (required) -phx-clickevent name fired when the reset button is pressed. The LiveView handler should reset all filter assigns to their default values.label(:string) - Button label text. Defaults to "Reset" when nil. Defaults tonil.class(:string) - Additional CSS classes for the button. Defaults tonil.Global attributes are accepted. HTML attributes forwarded to the
<button>element.
Renders a full-text search input with a magnifier icon.
Fires on_search via phx-change on every keystroke. Add phx-debounce
to your LiveView form or via :rest to debounce.
Example
<.filter_search
placeholder="Search invoices…"
on_search="search_invoices"
value={@search_query}
/>Attributes
placeholder(:string) - Input placeholder text shown when the field is empty. Defaults to"Search…".on_search(:string) (required) -phx-changeevent name fired on every keystroke. The LiveView receives%{"search" => query}(or whatever:nameis set to). Addphx-debounce="300"via:restto reduce server round-trips.value(:string) - Controlled value — pass the current search query assign from the LiveView. Defaults to"".name(:string) - Inputnameattribute — the param key the LiveView receives onphx-change. Defaults to"search".class(:string) - Additional CSS classes for the input wrapper. Defaults tonil.Global attributes are accepted. HTML attributes forwarded to the input wrapper div (e.g.
phx-debounce="300").
Renders a labelled native select dropdown.
The label is rendered as muted text to the left of the <select>. Native
<select> elements are used (not custom dropdowns) to preserve browser
accessibility, mobile behaviour, and keyboard navigation for free.
Example
<.filter_select
label="Priority"
name="priority"
options={[{"All", ""}, {"Critical", "critical"}, {"High", "high"}, {"Low", "low"}]}
value={@filter_priority}
on_change="filter_priority"
/>Attributes
label(:string) (required) - Visible label rendered to the left of the dropdown (e.g. "Status", "Role").name(:string) (required) - Selectnameattribute — the param key the LiveView receives onphx-change.options(:list) (required) - List of{label, value}tuples rendered as<option>elements. Convention:{"All", ""}as the first option clears the filter.options={[{"All", ""}, {"Active", "active"}, {"Inactive", "inactive"}]}value(:string) - Currently selected value — used to set theselectedattribute on the matching option. Defaults to"".on_change(:string) (required) -phx-changeevent name fired when the user changes the selection. The LiveView receives%{name => value}where name is:name.class(:string) - Additional CSS classes for the wrapper div. Defaults tonil.Global attributes are accepted. HTML attributes forwarded to the wrapper div.
Renders a checkbox toggle with a visible inline label.
Fires on_change via phx-change when the checkbox state changes.
The entire label text is clickable (clicking the text toggles the checkbox).
Example
<.filter_toggle
label="My tasks only"
name="mine_only"
checked={@mine_only}
on_change="toggle_mine_only"
/>
# LiveView handler
def handle_event("toggle_mine_only", %{"mine_only" => v}, socket) do
{:noreply, assign(socket, mine_only: v == "true")}
endAttributes
label(:string) (required) - Label text displayed beside the checkbox (e.g. "Show archived", "Mine only").name(:string) (required) - Checkboxnameattribute — the param key the LiveView receives.checked(:boolean) - Whether the checkbox is currently checked — bind to a LiveView boolean assign. Defaults tofalse.on_change(:string) (required) -phx-changeevent name. The LiveView receives%{name => "true" | "false"}. Note: HTML checkbox values are strings, so compare with== "true".class(:string) - Additional CSS classes for the label element. Defaults tonil.Global attributes are accepted. HTML attributes forwarded to the
<label>wrapper element.