Select
A dropdown for picking one or more values from a list.
<template> <Select :items="[{ value: 'a', label: 'Apple' }]" model-value="a" placeholder="Fruits" /></template>Props
| Name | Type | Default |
|---|---|---|
| items | ({ type: 'label'; label: string } | { value: string; label: string; description?: string; icon?: string; sublabel?: string; disabled?: boolean })[] | - |
| placeholder | string | - |
| icon | string | - |
| multiple | boolean | false |
| size | 'md' | 'sm' | 'xs' | 'md' |
| error | boolean | false |
| selectedLabel | string | - |
| triggerLabel | string | - |
| loading | boolean | false |
| disabled | boolean | false |
| triggerSurface | string | - |
| modelValue | string | string[] | - |
Slots
| Name | Description |
|---|---|
| leading | Content before the trigger's value, e.g. a status dot. The spinner replaces it while `loading`. |
| default | Single mode: replaces the trigger's value text, inside the value span (takes precedence over `triggerLabel`), e.g. to color it or show a value missing from `items`. |
| item-leading | Scoped `{ item }`. Replaces an option row's leading icon (e.g. a status dot). Not rendered for section-header entries. |
| item-label | Scoped `{ item }`. Replaces an option row's label (e.g. a status badge that carries its own text). Not rendered for section-header entries. |
Guidelines
Do
- Use this when the user must pick from a list that's too long to show all at once, or when screen space is limited.
- Use
multiplewhen more than one option can be selected - the trigger keeps showingplaceholderas a fixed label, with a small count badge appended once anything's selected. - When that placeholder is an instruction ("Select repositories"), pass
selected-labelwith the field's name ("Repositories"): once anything is selected the trigger reads the name beside the count instead of the instruction.
- Use the
#item-leading="{ item }"slot to replace an option's leading icon with custom content, such as a status dot. Without the slot, each item's owniconrenders as usual.#item-label="{ item }"replaces the label the same way, for a badge that carries its own text. - Group related options with a
{ type: 'label', label: '...' }section header entry - but only once there are 2+ groups to tell apart. - Inside a
UFormField, a validation error puts the field on the error surface by itself. Passerrorfor a state the form doesn't know; the trigger takes Input's red wash and border either way. - Use
#leadingfor content before the trigger's value (a status dot) andloadingwhile a picked value saves: the spinner replaces the leading content and the trigger is disabled until it settles. - Fill the default slot to render the trigger's value yourself, e.g. a label for a value that isn't a pickable option (a finding's "Closed" status).
- Write the placeholder by where the picker sits: in a filter or toolbar with no visible label, the bare field name ("Repository"); in a form under a label, "Select a/an x" ("Select a channel"). Once something is picked it shows the value, or with
multiplethe field name and the count (selected-label). - For a toolbar picker with no visible label, pass
trigger-labelwith the field's name: the trigger reads "Scan type: All" once something is picked, and the placeholder alone while nothing is. Keep anaria-labeltoo. - Always pass a
placeholderwhen the picker can start empty: there's no built-in default. - Don't use this for a short, fully-visible list of mutually-exclusive options - use
RadioGroupinstead. - Don't use this for independent on/off choices - use
Checkbox. - Don't use single mode when the user needs to return to "nothing selected" - a single-mode Select can only swap its value, never clear it. Use
Combobox(picking the selected option again clears it) or add an explicit "Any" option. - Don't add a section header to a single, ungrouped list - a lone
{ type: 'label' }entry has nothing to distinguish it from, so it just adds noise above the options. - Don't pass
:ui- it would replace the whole design-system look (trigger, panel and option rows), so the type-check rejects it. Select and Combobox triggers share one resting surface with the secondary Button, and every picker shares one option-row style. Don't restyle the trigger throughclasseither: the one sanctioned exception istrigger-surface, for a picker that renders as a badge (the severity picker). - Don't use an example name ("e.g. acme/web"), an ellipsis, or a generic "Select something" as the placeholder: an example reads as already picked, and a generic one doesn't say what to pick.
Examples
Variants
Multiple select
Selecting more than one item at once, shown as a count.
<template> <Select v-model="selected" :items="[ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'blueberry', label: 'Blueberry' }, { value: 'grapes', label: 'Grapes' }, { value: 'pineapple', label: 'Pineapple' }, ]" multiple placeholder="Fruits" /></template>Sizes
Sizes
The three trigger height steps - xs, sm, and md.
<template> <Select v-model="selected" :items="items" size="xs" placeholder="Fruits" /> <Select v-model="selected" :items="items" size="sm" placeholder="Fruits" /> <Select v-model="selected" :items="items" size="md" placeholder="Fruits" /></template>With icons and slots
With icon
A leading icon on the trigger for extra context.
<template> <Select v-model="selected" :items="[ { value: 'open', label: 'Open' }, { value: 'true_positive', label: 'Triaged' }, { value: 'resolved', label: 'Resolved' }, { value: 'accepted_risk', label: 'Accepted Risk' }, { value: 'false_positive', label: 'False Positive' }, ]" icon="i-lucide-circle-dot" placeholder="Status" /></template>Composition
Multiple sections
Items grouped under more than one section header.
<template> <Select v-model="selected" :items="[ { type: 'label', label: 'Fruits' }, { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { type: 'label', label: 'Vegetables' }, { value: 'carrot', label: 'Carrot' }, { value: 'potato', label: 'Potato' }, ]" placeholder="Ingredients" /></template>With sublabel
A monospace value shown inline next to each item's own label.
<template> <Select v-model="selected" :items="[ { value: 'apple', label: 'Apple', sublabel: 'AAPL' }, { value: 'banana', label: 'Banana', sublabel: 'BNNA' }, { value: 'blueberry', label: 'Blueberry', sublabel: 'BLBY' }, ]" placeholder="Fruits" /></template>