> 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/select.md).

# x-select

Directives and a magic for select-only dropdowns: a listbox in a floating panel, with a button that shows the current selection.

Directives and a magic for select-only dropdowns: a listbox in a floating panel, with a button that shows the current selection.

| Directive / magic       | What it does                                                                                                                     |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `x-select`              | The root. Modifiers and expression pass to `x-popover`; locks page scroll while open                                             |
| `x-select:button`       | Opens the panel (`aria-haspopup="listbox"`). ArrowDown opens; typing opens and jumps to a match                                  |
| `x-select:panel`        | The floating panel. Modifiers are `x-float` positioning modifiers (default `bottom-start.flip.match.update.shift.fit-y.pad.5px`) |
| `x-select:options`      | The options container (`role="listbox"`)                                                                                         |
| `x-select:option`       | One option (`role="option"`). Its `value` attribute is the value; its children render as the selection                           |
| `x-select:display`      | Shows the selected option's content inside the button                                                                            |
| `x-select:hidden-input` | A hidden `<input>` with the form value                                                                                           |
| `x-select:label`        | The label element for the select                                                                                                 |
| `$select`               | Everything on `$listbox` and `$popup`, merged                                                                                    |

The keyboard behavior matches a native `<select>`: ArrowDown on the button opens the panel and focus lands on the selected option, typing a printable character opens the panel and focuses the first option starting with it (type-ahead also works inside the open list), arrow keys move through the enabled options, and Enter or Space selects. Selecting closes the panel and returns focus to the button; Escape closes without selecting. The button is labelled by its display and label, and when no `x-select:label` exists the plugin finds a `<label for="…">` pointing at the button and uses that.

In Analog, render the `custom-select` snippet with `custom-select-option` children. It includes the panel styles, transitions, and lazy initialization. The examples below show the generated structure.

## Examples

### A basic select

Use `x-select:button` with `x-select:display`, a hidden input, and one `x-select:option` per choice:

```html
<div x-select>
  <span x-select:label class="sr-only">Sort by</span>
  <input type="hidden" name="sort_by" value="manual" x-select:hidden-input>

  <div x-select:panel x-cloak>
    <ul x-select:options>
      <li x-select:option value="manual">Featured</li>
      <li x-select:option value="price-ascending">Price, low to high</li>
    </ul>
  </div>

  <button x-select:button type="button">
    <span x-select:display>Featured</span>
  </button>
</div>
```

The hidden input's `value` attribute sets the initial selection, and its `name` puts the value in the surrounding form. The display clones the selected option's children, so a swatch, icon, or styled text inside the option also appears in the button.

### A placeholder

Use a hidden, disabled option with an empty value to show a prompt until the shopper picks:

```html
<li x-select:option value="" aria-disabled="true" aria-selected="true" hidden>
  Choose a size
</li>
```

The `custom-select` snippet renders this when it gets a `label` and no initial value: `hidden` keeps it out of the open list, `aria-disabled` keeps it unselectable, and the empty value means the form submits nothing until a real choice is made.

### Submitting on change

Use `@change` on the root to react when the selection changes:

```html
<div x-select @change="$refs.collectionFiltersForm.requestSubmit()">
  …
</div>
```

The collection sort control uses this event. Selecting an option updates the hidden input, bubbles `change` to the root, and submits the filter form. The new value is in `$event.detail`.

### Positioning the panel

Pass `x-float` modifiers on `x-select:panel` to change where the panel opens:

```html
<div x-select:panel.fixed.top.offset.5px.pad.10px.match.flip.shift.fit-y x-cloak>…</div>
```

Passing any modifier replaces the whole default string. `match` sizes the panel to the button's width and `fit-y` caps its height to the viewport. The `custom-select` snippet builds this string from its `placement`, `offset`, and `match` parameters. The full modifier list lives on the [x-float](/reference/alpine/float.md) page.

### Labelling the select

Use `x-select:label`, or point a `<label>` at the button with `for`:

```html
<label for="my-select">Sort by</label>
<div x-select>
  <button x-select:button id="my-select">…</button>
  …
</div>
```

With `for`, the plugin finds the label, gives it an `id` if it has none, and adds it to the button's `aria-labelledby`. The `custom-select` snippet uses an sr-only `x-select:label` instead, since its selects sit in contexts where the label would repeat visible text.

## Options

The root takes no options of its own. Modifiers and the expression pass through to [x-popover](/reference/alpine/popover.md) and from there to [x-popup](/reference/alpine/popup.md), so `isOpen` with a get/set pair works here too. The selection API is [x-listbox](/reference/alpine/listbox.md); both magics merge into `$select`.
