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"]} />

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))}
end

The 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>

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)

AttributeTypeDefaultDescription
idstringnilUnique identifier
namestringnilForm field name
valuestringnilSelected value
placeholderstring"Select an item"Placeholder text
optionslist[]List of options (strings, tuples, or groups)
searchablebooleanfalseEnable search/filter functionality
search_eventstringnilLiveView event for server-backed search
search_debounceinteger300Delay in milliseconds before a search event is sent
classstring"w-fit"Additional CSS classes
labelstringnilLabel text
fieldFormFieldnilPhoenix form field struct
errorslist[]Error messages shown below the select

Slots

SlotDescription
inner_blockCustom select items using <.select_item>
headerContent to display at the top of dropdown
footerContent to display at the bottom of dropdown

Summary

Functions

Renders the select trigger and option list.

Renders a custom option inside select/1.

Renders the browser-owned search input used by a searchable select.

Functions

map_field(assigns)

select(assigns)

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 to nil.
  • name (:string) - Defaults to nil.
  • value (:string) - Defaults to nil.
  • placeholder (:string) - Defaults to "Select an item".
  • options (:list) - Defaults to [].
  • searchable (:boolean) - Defaults to false.
  • search_event (:string) - Defaults to nil.
  • search_debounce (:integer) - Defaults to 300.
  • class (:string) - Use w-fit for a width that fits the selected item. Defaults to "w-full".
  • label (:string) - Defaults to nil.
  • field (Phoenix.HTML.FormField) - a form field struct retrieved from the form, for example: @form[:email]. Defaults to nil.
  • errors (:list) - a list of error strings to display below the select. Defaults to [].
  • show_errors (:boolean) - Defaults to true.
  • Global attributes are accepted.

Slots

  • inner_block
  • header
  • footer

select_icon(assigns)

Attributes

  • class (:string) - Defaults to "size-4".
  • Global attributes are accepted.

select_item(assigns)

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 to nil.
  • class (:string) - Defaults to "".

Slots

  • inner_block

select_search(assigns)

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 to nil.
  • listbox_id (:string) - Defaults to nil.
  • Global attributes are accepted.