PUI. Select
(pui v1.0.0)
Copy Markdown
A customizable select dropdown with search, grouping, and form-aware errors.
Basic Usage
<.select id="food" name="food">
<.select_item value="apple">Apple</.select_item>
<.select_item value="banana">Banana</.select_item>
<.select_item value="orange">Orange</.select_item>
</.select>With Options List
Pass options as a list for automatic rendering:
<.select id="food" name="food" options={["Apple", "Banana", "Orange"]} />With Value/Label Pairs
<.select id="food" name="food" options={[
{"apple", "Apple"},
{"banana", "Banana"}
]} />With Groups
<.select id="food" name="food" options={[
{"Fruits", ["Apple", "Banana"]},
{"Vegetables", [{"carrot", "Carrot"}]}
]} />Searchable Select
<.select id="food" name="food" searchable={true} options={["Option 1", "Option 2"]} />Server-backed Search
Set search_event to let the owning LiveView query options from the server.
The hook sends the query after the configurable debounce, and the LiveView
re-renders options in response:
<.select
id="country"
name="country_id"
searchable={true}
search_event="search_countries"
options={@country_options}
/>
def handle_event("search_countries", %{"query" => query}, socket) do
{:noreply, assign(socket, :country_options, Countries.search(query))}
endThe event payload also includes select_id, name, and the current selected
value. An empty query restores the default options without clearing the
selected value. The host should keep the selected option in the returned list
when its label must remain visible.
With Label
<.select id="food" name="food" label="Select Food">
<:option value="apple">Apple</:option>
</.select>With Phoenix Form
<.form for={@form}>
<.select field={@form[:category]} options={@categories} />
</.form>When field is provided, the component derives id, name, value, and
validation errors from the form field and renders those errors below the
trigger once the field has been used.
Manual Errors
<.select
id="category"
name="category"
label="Category"
errors={["Please choose a category."]}
options={["Design", "Engineering", "Marketing"]}
/>With Icons
<.select id="food" name="food">
<.select_item value="apple">
<.icon name="hero-apple" class="size-4" /> Apple
</.select_item>
</.select>With Footer
Add custom content at the bottom of the dropdown, such as action buttons:
<.select id="items" name="items" searchable={true}>
<.select_item value="item-1">Item One</.select_item>
<.select_item value="item-2">Item Two</.select_item>
<:footer>
<div class="border-t border-border p-2">
<button type="button" phx-click="add-new" class="text-sm text-primary">
+ Add New Item
</button>
</div>
</:footer>
</.select>Attributes (select/1)
| Attribute | Type | Default | Description |
|---|---|---|---|
id | string | nil | Unique identifier |
name | string | nil | Form field name |
value | string | nil | Selected value |
placeholder | string | "Select an item" | Placeholder text |
options | list | [] | List of options (strings, tuples, or groups) |
searchable | boolean | false | Enable search/filter functionality |
search_event | string | nil | LiveView event for server-backed search |
search_debounce | integer | 300 | Delay in milliseconds before a search event is sent |
class | string | "w-fit" | Additional CSS classes |
label | string | nil | Label text |
field | FormField | nil | Phoenix form field struct |
errors | list | [] | Error messages shown below the select |
Slots
| Slot | Description |
|---|---|
inner_block | Custom select items using <.select_item> |
header | Content to display at the top of dropdown |
footer | Content to display at the bottom of dropdown |
Summary
Functions
Renders the select trigger and option list.
Use options for the common tuple/string-based API, or provide custom
select_item/1 entries through the default slot. Both manual errors and
field-derived validation errors are rendered below the component.
Examples
<.select
id="food"
name="food"
label="Favorite Food"
options={["Pizza", "Pasta", "Sushi"]}
/>
<.select
field={@form[:category]}
label="Category"
searchable={true}
options={["Design", "Engineering", "Marketing"]}
/>
<.select
id="category"
name="category"
label="Category"
errors={["Please choose a category."]}
options={["Design", "Engineering", "Marketing"]}
/>Attributes
id(:string) - Defaults tonil.name(:string) - Defaults tonil.value(:string) - Defaults tonil.placeholder(:string) - Defaults to"Select an item".options(:list) - Defaults to[].searchable(:boolean) - Defaults tofalse.search_event(:string) - Defaults tonil.search_debounce(:integer) - Defaults to300.class(:string) - Use w-fit for a width that fits the selected item. Defaults to"w-full".label(:string) - Defaults tonil.field(Phoenix.HTML.FormField) - a form field struct retrieved from the form, for example: @form[:email]. Defaults tonil.errors(:list) - a list of error strings to display below the select. Defaults to[].show_errors(:boolean) - Defaults totrue.- Global attributes are accepted.
Slots
inner_blockheaderfooter
Attributes
class(:string) - Defaults to"size-4".- Global attributes are accepted.
Renders a custom option inside select/1.
Examples
<.select_item value="edit">
<.icon name="hero-pencil" class="size-4" /> Edit
</.select_item>Attributes
value(:string) (required)id(:string) - Defaults tonil.class(:string) - Defaults to"".
Slots
inner_block
Renders the browser-owned search input used by a searchable select.
The styled select supplies this part automatically. Use
PUI.Select.Primitive.search/1 when composing a headless select.
Attributes
id(:string) - Defaults tonil.listbox_id(:string) - Defaults tonil.- Global attributes are accepted.