> For the complete documentation index, see [llms.txt](https://analog.groupthought.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://analog.groupthought.com/reference/alpine/listbox.md).

# x-listbox

Directives and magics for accessible listboxes, including options, selection state, and form values.

Directives and magics for accessible listboxes, including options, selection state, and form values.

| Directive / magic        | What it does                                                                                       |
| ------------------------ | -------------------------------------------------------------------------------------------------- |
| `x-listbox`              | The root. Holds the selected value                                                                 |
| `x-listbox:options`      | The options container (`role="listbox"`). One delegated listener selects on click, Enter, or Space |
| `x-listbox:option`       | One option (`role="option"`). Its `value` attribute is the option's value                          |
| `x-listbox:hidden-input` | A hidden `<input>` with the selected form value                                                    |
| `x-listbox:display`      | Shows a copy of the selected option's content                                                      |
| `$listbox`               | `selectedValue`, `select(value)`, `deselect()`, `isSelected(value)`, `options`, `enabledOptions`   |
| `$listboxOption(el)`     | Helpers for one option element: `isSelected`, `isDisabled`, `select()`                             |

Arrow keys move focus through enabled options with a roving tabindex. The selected option is the list's single tab stop. Enter, Space, or a click selects the focused option. The plugin updates `aria-selected` and skips options with `aria-disabled="true"`. Options aren't Alpine components, so a large option list doesn't initialize a component for every item.

Analog uses this plugin through [x-select](/reference/alpine/select.md), which puts the listbox in a floating panel. Variant pickers and collection sorting use selects.

## Examples

### A basic listbox

Use `x-listbox:option` with a `value` attribute on each option, and `x-listbox:hidden-input` to carry the selection into a form:

```html
<div x-listbox>
  <input type="hidden" name="size" x-listbox:hidden-input>
  <ul x-listbox:options>
    <li x-listbox:option value="s">Small</li>
    <li x-listbox:option value="m">Medium</li>
    <li x-listbox:option value="l">Large</li>
  </ul>
</div>
```

Style the states through the ARIA attributes the plugin maintains, for example `aria-selected:shadow` and `aria-disabled:opacity-50`.

### Disabled options

Use `aria-disabled="true"` to keep an option visible but unselectable:

```html
<li x-listbox:option value="xl" aria-disabled="true">Extra large</li>
```

Clicks and keyboard activation ignore the option, and arrow keys skip past it.

### Reacting to a selection

Use `@change` to run code when the selection changes:

```html
<div x-listbox @change="applyFilter($event.detail)">
  <ul x-listbox:options>
    <li x-listbox:option value="price-asc" @change="track('cheapest first')">
      Price, low to high
    </li>
    …
  </ul>
</div>
```

Selecting fires `change` and `input` events with the value in `event.detail`. The events fire from the selected option and bubble. A handler on one option sees only that selection. A handler on the root sees every change. The product variant pickers use the option-level handler. Events wait a tick so the hidden input has the new value when a handler reads the form.

### Reading and writing the selection

Use `$listbox` anywhere inside the root to drive the selection from code:

```html
<button @click="$listbox.select('m')">Pick medium</button>
<span x-text="$listbox.selectedValue"></span>
```

`select(value)` fires the change events; pass `false` as the second argument to set the value silently. `deselect()` clears the selection, and `x-listbox:display` empties with it.

## Options

The root takes no options object. The initial selection comes from the hidden input's `value` attribute (via `x-model.fill`). To put a listbox in a floating panel behind a button, use [x-select](/reference/alpine/select.md), which composes this plugin with [x-popover](/reference/alpine/popover.md).
