Listbox

A single-select value picker with keyboard navigation, intelligent positioning, and Phoenix form integration. Use a listbox when users need to select one value from a predefined set of options.

Unlike Dropdown, which provides an action menu with role="menu", Listbox uses role="listbox" and works like a native <select>. It includes smart positioning with Floating UI .

Quick Start

The most basic listbox requires three components: .listbox, .listbox_trigger, and .listbox_options with .listbox_option elements.

This example starts with "Cherry" selected through the value attribute. Selecting another option updates the .listbox_value content immediately.

Styling

Popup width

Width is controlled by CSS. --reference-width tracks the width of the trigger, or the element selected by reference, while open. Apply width classes to .listbox_options.

Use min-w-[var(--reference-width)] for a minimum width, or w-[var(--reference-width)] for an exact match.

The Quick Start example uses min-w-[var(--reference-width)] to keep the options at least as wide as the trigger.

Form Integration

.listbox renders a hidden <input> with the given name. Selecting an option updates the input and dispatches input and change events. This makes a parent form's phx-change event run as it does for a native form field. Use .listbox_value to render the current value or a placeholder in the trigger.

Selected fruit (from server state): none

The selected fruit below the trigger comes from server state. It confirms that the phx-change event completed while the trigger value updated immediately.

Disabled Options

Disable a listbox option with the disabled=true attribute. Disabled options cannot receive focus or be selected.

Disabled items use aria-disabled="true" and data-disabled="true" attributes for accessibility and styling. Use the data-disabled: variant to style disabled items differently, such as reducing opacity or changing text color.

Disabled Listbox

Disable the entire listbox with disabled=true on .listbox. The hidden input is disabled in the initial HTML, excluding its value from form submission. When the JavaScript hook mounts, it disables the trigger button, preventing interaction and removing it from the tab order. LiveView updates keep the state synchronized and close an open listbox when it becomes disabled. Re-enabling the field preserves its selected value.

Submitted params: not submitted yet

To style the disabled state, use the in-data-disabled: variant on the trigger button. The regular data-disabled: does not work because Prima is unable to copy the data-disabled="true" attribute over to the trigger button in the server-rendered HTML.

Setting the Value Client-Side

Use Prima.Listbox.JS.set_value/3 to change the value without a server round-trip, for example to reset a field that client-side code hides. It updates the hidden input, the displayed value and the selection markers.

Like setting .value on a native <select>, it dispatches no events by default, so phx-change doesn't fire and the server keeps its old value. Pass notify: true to dispatch input and change like a user selection. Values that match no option are ignored. From plain JavaScript, dispatch a prima:listbox:set-value event on the listbox element with the value in its detail.

Keyboard Interaction

The listbox provides keyboard navigation that follows standard ARIA patterns. Use the arrow keys to move between options and Enter or Space to select an option.

Key Description
When the trigger button is focused:
Enter / Space Opens the listbox and focuses the current selection (or the first option, if none)
↓ Opens the listbox and focuses the first non-disabled option
↑ Opens the listbox and focuses the last non-disabled option
When the listbox is open:
Esc Closes the listbox without changing the selection, and returns focus to the trigger
↑ / ↓ Focuses the previous/next non-disabled option (wraps around)
Home / End Focuses the first/last non-disabled option
Enter / Space Selects the focused option, updates the displayed value, and closes the listbox
A-Z / 0-9 Focuses the first option that starts with the typed character. Repeated presses cycle through matching options.