Skip to content

Select

Allows users to choose one or more items from a predefined list of options. This component provides a compact way to present choices without overwhelming the interface.

Usage

Use the Select component without a search option when there are a limited number of options, ideally 12 or fewer. For longer lists, enable the search feature.

Keyboard Handling

The user can use the following keyboard shortcuts in order to navigate trough the select:

  • Up / Down — Moves focus to the previous or next option.
  • Home / Page Up — Moves focus to the first option.
  • End / Page Down — Moves focus to the last option.
  • Enter / Space — Selects the focused option. If the list is closed, opens it.
  • Esc — Closes the list without changing the current selection.
  • Tab — Closes the list and moves focus to the next focusable element.
  • Character keys (A–Z) — Moves focus to the next option whose label starts with the typed character.

API Reference

Component attubute(s)

x-h-select
x-h-select-input
x-h-select-content
x-h-select-search
x-h-select-group
x-h-select-label
x-h-select-option
x-h-select-separator

Attributes

x-h-select

AttributeTypeRequiredDescription
data-sizesm
default
falseChanges the size of the select button.

x-h-select-input

AttributeTypeRequiredDescription
data-idstringfalseSince the native input is hidden, this attribute provides an ID for proper labeling and accessibility. The value is forwarded to the actual select trigger element.

x-h-select-content

AttributeTypeRequiredDescription
data-alignbottom-start
bottom
bottom-end
right-start
right
right-end
left-start
left
left-end
top-start
top
top-end
falseAligns the select body relative to the trigger.

x-h-select-option

AttributeTypeRequiredDescription
selfstringfalseSets the label of the option. Either a string literal or a variable.
data-valuebooleanfalseSets the value of the option.
data-disabledbooleanfalseDisables the option.
AttributeTypeRequiredDescription
data-filterstarts-with
contains
contains-each
none
falseDefines the search matching strategy. Use none to disable built-in filtering and implement custom search behavior. With the 'contains-each' filter, search terms are separated using space.

Examples

With model


html
<div x-data="selectData">
  <div x-h-select>
    <input x-h-select-input :placeholder="placeholder" x-model="selected" />
    <div x-h-select-content>
      <div x-h-select-search></div>
      <div x-h-select-group>
        <div x-h-select-label>Fruits</div>
        <template x-for="option in items">
          <div x-h-select-option="option.label" :data-value="option.value"></div>
        </template>
      </div>
    </div>
  </div>
</div>
<script>
  const originalItems = [
    { label: 'Apple', value: 'apple' },
    { label: 'Banana', value: 'banana' },
    { label: 'Blueberry', value: 'blueberry' },
    { label: 'Grapes', value: 'grapes' },
    { label: 'Pineapple', value: 'pineapple' },
    { label: 'Jamaican tangelo', value: 'jamaicanTangelo' },
  ];
  Alpine.data('selectData', () => ({
    selected: 'banana',
    placeholder: 'Select',
    items: structuredClone(originalItems),
    addFromSearch(event) {
      let nItems = structuredClone(originalItems);
      nItems.forEach((element) => {
        element.label = `${event.target.value}${element.label}`;
      });
      this.items = nItems;
    },
  }));
</script>

Multiple

The input automatically switches modes based on the model. If you want to select multiple items, pass an array as the model.

html
<div x-data="selectData">
  <div x-h-select>
    <input x-h-select-input :placeholder="placeholder" x-model="selected" />
    <div x-h-select-content>
      <div x-h-select-search></div>
      <div x-h-select-group>
        <div x-h-select-label>Fruits</div>
        <template x-for="option in items">
          <div x-h-select-option="option.label" :data-value="option.value"></div>
        </template>
      </div>
    </div>
  </div>
</div>
<script>
  const originalItems = [
    { label: 'Apple', value: 'apple' },
    { label: 'Banana', value: 'banana' },
    { label: 'Blueberry', value: 'blueberry' },
    { label: 'Grapes', value: 'grapes' },
    { label: 'Pineapple', value: 'pineapple' },
    { label: 'Jamaican tangelo', value: 'jamaicanTangelo' },
  ];
  Alpine.data('selectData', () => ({
    selected: ['apple', 'banana'],
    placeholder: 'Select',
    items: structuredClone(originalItems),
    addFromSearch(event) {
      let nItems = structuredClone(originalItems);
      nItems.forEach((element) => {
        element.label = `${event.target.value}${element.label}`;
      });
      this.items = nItems;
    },
  }));
</script>

No model


html
<div x-h-select>
  <input x-h-select-input placeholder="Select" />
  <div x-h-select-content>
    <div x-h-select-option="'Option 1'" data-value="1"></div>
    <div x-h-select-option="'Option 2'" data-value="2"></div>
    <div x-h-select-option="'Option 3'" data-value="3"></div>
    <div x-h-select-option="'Option 4'" data-value="4" data-disabled="true"></div>
    <div x-h-select-option="'Option 5'" data-value="5"></div>
  </div>
</div>

With groups


html
<div x-h-select>
  <input x-h-select-input placeholder="Select" />
  <div x-h-select-content>
    <div x-h-select-group>
      <div x-h-select-label>First two options</div>
      <div x-h-select-option="'Option 1'" data-value="1"></div>
      <div x-h-select-option="'Option 2'" data-value="2"></div>
    </div>
    <div x-h-select-group>
      <div x-h-select-label>The rest</div>
      <div x-h-select-option="'Option 3'" data-value="3"></div>
      <div x-h-select-option="'Option 4'" data-value="4"></div>
      <div x-h-select-separator></div>
      <div x-h-select-option="'Option 5'" data-value="5"></div>
    </div>
  </div>
</div>