# Select

> A single or multiple choice from a dropdown.

## Examples

### Basic

```vue
<script setup lang="ts">
const timezone = ref<string>("Europe/London");
const timezones = [
  { label: "Lagos", value: "Africa/Lagos", offset: "UTC+1" },
  { label: "London", value: "Europe/London", offset: "UTC+0" },
  { label: "New York", value: "America/New_York", offset: "UTC−5" },
  { label: "Tokyo", value: "Asia/Tokyo", offset: "UTC+9" },
  { label: "Sydney", value: "Australia/Sydney", offset: "UTC+10", disabled: true },
];
</script>

<template>
  <div class="demo-field">
    <UiSelect v-model="timezone" :options="timezones" label="Timezone">
      <template #option="{ option, selected }">
        <span class="option-label">{{ option.label }}</span>
        <span class="option-detail">{{
          timezones.find((t) => t.value === option.value)?.offset
        }}</span>
        <Icon v-if="selected" name="check" :size="14" class="option-check" />
      </template>
    </UiSelect>
  </div>
</template>
```

### Multiple

```vue
<script setup lang="ts">
const labels = ref(["bug", "docs"]);
const options = [
  { label: "Bug", value: "bug", color: "oklch(0.68 0.19 25)" },
  { label: "Feature", value: "feature", color: "oklch(0.72 0.15 250)" },
  { label: "Docs", value: "docs", color: "oklch(0.75 0.14 150)" },
  { label: "Question", value: "question", color: "oklch(0.8 0.14 85)" },
];
const colorOf = (value: string | number) => options.find((o) => o.value === value)?.color;
</script>

<template>
  <div class="demo-field">
    <UiSelect v-model="labels" :options="options" label="Labels" placeholder="Add labels" multiple>
      <template #value="{ selected, placeholder }">
        <span v-if="selected.length" class="chips">
          <span v-for="option in selected" :key="option.value" class="chip">
            <span class="dot" :style="{ background: colorOf(option.value) }" />
            {{ option.label }}
          </span>
        </span>
        <template v-else>{{ placeholder }}</template>
      </template>
      <template #option="{ option, selected }">
        <span class="dot" :style="{ background: colorOf(option.value) }" />
        <span class="option-label">{{ option.label }}</span>
        <Icon v-if="selected" name="check" :size="14" class="option-check" />
      </template>
    </UiSelect>
  </div>
</template>
```

## API

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | `SelectOption<T>[]` | `[]` | Options to choose from. |
| `error` | `string \| boolean` | — | `true` marks the control invalid; a string also shows it as the message, replacing the hint. |
| `placeholder` | `string` | `"Select an option"` | Shown when nothing is selected. |
| `multiple` | `boolean` | `false` | Allow several selections; `v-model` becomes an array and the list stays open while picking. |
| `optional` | `boolean` | `false` | Appends the `optional` translation to the label. |
| `disabled` | `boolean` | `false` | Disables the control. |
| `label` | `string` | — | Visible label, linked to the control. |
| `hint` | `string` | — | Help text under the control, announced as its description. |
| `name` | `string` | — | Form field name: submits the value with a native form, via hidden inputs. |
| `id` | `string` | — | Id for the control; generated with `useId()` when omitted. |
| `modelValue` | `T \| T[]` | — | The selected value, or values with `multiple`. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `[value: T \| T[] \| undefined]` | `v-model:modelValue` update. |

| Slot | Props | Description |
| --- | --- | --- |
| `value` | `{ selected: SelectOption<T>[]; placeholder: string; }` | Trigger content, replacing the selected label or placeholder. |
| `option` | `{ option: SelectOption<T>; selected: boolean; active: boolean; }` | Option content. |
| `chevron` | — | Replaces the trigger's chevron. |
| `check` | — | Replaces the selected option's check mark. |

Types:

```ts
export interface SelectOption<T extends SelectValue = SelectValue> {
  label: string;
  value: T;
  disabled?: boolean;
}

export type SelectValue = string | number;
```

## Styling

- Parts (`[data-part="…"]`): `field`, `field-label`, `field-optional`, `select-control`, `select-trigger`, `select-value`, `select-chevron`, `select-listbox`, `select-option`, `select-option-check`, `select-option-label`, `field-message`
- State attributes: `data-field-error`, `data-field-name`, `data-select-open`, `data-select-error`, `data-select-placeholder`, `data-select-active`, `data-field-tone`

## Keyboard

| Keys              | Action                                               |
| ----------------- | ---------------------------------------------------- |
| `↓` / `↑`         | Open, then move between options                      |
| `Enter` / `Space` | Open, or select the active option                    |
| `Home` / `End`    | First / last option                                  |
| Any letter        | Jump to an option by label; repeat a letter to cycle |
| `Tab`             | Keep the active option and close                     |
| `Escape`          | Close                                                |

## Accessibility

- Focus stays on the trigger; the active option is set with `aria-activedescendant`.
