Skip to content

Select ​

Select is a dropdown for choosing from a list. It is built on reka-ui's listbox, supports single and multiple selection, options given as objects or primitives, an optional clear control, and two sizes.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Select } from '@myghf/ui'

const department = ref<string | null>(null)
const options = [
  { label: 'Cardiology', value: 'cardiology' },
  { label: 'Radiology', value: 'radiology' },
]
</script>

<template>
  <Select v-model="department" :options="options" placeholder="Choose" clearable />
</template>

Examples ​

Single selection ​

optionLabel / optionValue name the object keys (both default to label / value). clearable adds a clear button that emits null (or [] when multiple).

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Select } from '@myghf/ui'

const department = ref<string | null>('cardiology')
const options = [
  { label: 'Cardiology', value: 'cardiology' },
  { label: 'Radiology', value: 'radiology' },
  { label: 'Paediatrics', value: 'paediatrics' },
]
</script>

<template>
  <div class="grid max-w-xs gap-4">
    <Select
      v-model="department"
      :options="options"
      placeholder="Choose a department"
      clearable
      aria-label="Department"
    />

    <Select
      :model-value="null"
      :options="options"
      placeholder="Disabled"
      disabled
      aria-label="Disabled department"
    />
  </div>
</template>

Multiple selection ​

With multiple, the model is an array of values and the trigger shows the selected labels joined by commas. Clearing emits an empty array.

Selected: cardiology

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Select } from '@myghf/ui'

const selected = ref<string[]>(['cardiology'])
const options = [
  { label: 'Cardiology', value: 'cardiology' },
  { label: 'Radiology', value: 'radiology' },
  { label: 'Paediatrics', value: 'paediatrics' },
  { label: 'Neurology', value: 'neurology' },
]
</script>

<template>
  <div class="max-w-sm">
    <Select
      v-model="selected"
      :options="options"
      multiple
      clearable
      placeholder="Choose specialties"
      aria-label="Specialties"
    />
    <p class="mt-3 text-sm text-muted">
      Selected: {{ selected.join(', ') || 'none' }}
    </p>
  </div>
</template>

Props ​

PropTypeDefaultDescription
modelValueAcceptableValue | AcceptableValue[]—Selected value, or an array of values when multiple.
optionsunknown[]—Choices. Objects (read via optionLabel / optionValue) or primitives.
optionLabelstring'label'Key whose value is shown as an option's label (objects only).
optionValuestring'value'Key whose value is emitted as an option's value (objects only).
placeholderstring''Text shown when nothing is selected.
clearablebooleanfalseShows a clear button while a value is selected.
disabledbooleanfalseDisables the trigger and the popup.
multiplebooleanfalseAllows multiple selections; the model becomes an array.
size'sm' | 'default''default'Trigger height.
invalidbooleanfalseApplies the error border and focus ring. Visual only; it does not set aria-invalid.

Events ​

EventPayloadDescription
update:modelValueunknownEmitted when the selection changes: a single value, an array when multiple, null/[] when cleared.

Slots ​

Select renders no slots. Options are data-only (options + optionLabel / optionValue); there is no per-option slot for custom content.

Exposed methods ​

None. Select does not call defineExpose.

Accessibility ​

  • The trigger and popup come from reka-ui's listbox: the trigger exposes combobox semantics, the popup is a listbox, and typeahead, arrow-key navigation, Enter, and Esc are handled.
  • Extra attributes (aria-label, aria-labelledby, id, class) fall through to the trigger button. Give it a name with aria-label or aria-labelledby, since it renders no visible <label> of its own:
    vue
    <Select v-model="department" :options="options" aria-label="Department" />
  • The built-in clear control is a <button> with aria-label="Clear selection"; it is focusable and does not close the popup.
  • invalid only changes colour. It does not set aria-invalid — add it via a fall-through attribute when the field is in an error state.
  • Visual content is limited to option labels. Because there is no option slot, richer rows (icons, descriptions, grouping) are not supported.

Dark mode & RTL ​

  • The trigger and popup use semantic tokens (bg-surface, text-foreground, border-border, shadow-popover); the selected check uses primary-600 and highlighted rows use surface-muted, so both themes are covered.
  • Layout is logical: the value truncates, the trailing controls use ms-auto, the check indicator uses end-2, and option text reserves space with pe-8. Under RTL the chevron and clear button stay at the inline-end edge and the popup content mirrors automatically.

Released under the MIT License.