Combobox
A field that searches a list and keeps what is chosen, one value or several. The options may be flat, grouped or separated, and they may arrive from a server as the reader types.
Usage
Examples
Label and hint
label renders a descriptive text above the field, and hint renders a helper text below it.
Type to narrow the list down. Accents are ignored, so reunion finds Réunion.
Sizes
Sets the field height to 32, 40, or 48 pixels. The compact prop reduces this height by 4px.
States
disabled makes the field unusable. readonly prevents changes but keeps the field focusable. invalid marks the field as having an error. loading displays a loading indicator. emptyText defines the message shown when there are no options. clearable adds an icon to clear the selection.
The cross empties the selection and the search at once
For a rule the browser cannot check by itself
Set by your subscription
Placement
Sets the preferred opening direction (above or below the field) for the options list panel.
Groups and separators
The options prop accepts a flat list, or can be structured with named groups and separators.
Multiple selection
multiple allows selecting several values, which are displayed as removable chips inside the field.
Click elsewhere: the search field folds away and only the chips remain
Values as text
display="text" shows the chosen values as their labels joined by commas, on one line cut short with an ellipsis. The field keeps the height of an ordinary control, and under the focus the line leaves at least half of it to the search. A value is removed by unticking it in the list, with Backspace on an empty search, or with the clearable cross.
One line of labels, cut short when it runs out of room
Values shown while folded
max keeps the first chosen values in view and sums the rest up as "+X", in chips or in text alike. It applies while the field is out of focus: focused, every value comes back so it can be seen and removed. overflowText rephrases the count, and the #overflow slot replaces it.
Two chips, the rest counted until the field is focused
Field icon
iconStart displays an icon at the beginning of the field. iconStartLabel provides an accessible label if the icon is made interactive.
Sorted A to Z.
Option icons
An option's icon property displays an icon alongside its label in the dropdown list.
Asynchronous search
Disabling filter shows options exactly as provided by the source. searchDebounce sets the delay in milliseconds before emitting the search term.
The list is narrowed by the source, so local filtering is turned off
Infinite scroll
hasMore indicates that more pages are available, triggering a load-more event when the end of the list comes into view.
Scroll to the foot of the list: the next page is asked for as the end comes into view
Custom options
The #option slot allows customizing the content and layout of an option row (e.g., adding a badge or a second line).
Custom chips
The #chip slot allows customizing the appearance of the selected value chips.
API
Props
| Prop | Type | Default |
|---|---|---|
options | ComboboxItem[] | none |
| What the list offers. An entry may be an option, a named block of options, or a separator; a plain list of options remains perfectly valid. | ||
multiple | boolean | false |
Allows several values to be chosen, which makes the value a list and shows what has been chosen inside the field, as chips or as text depending on display. | ||
display | ComboboxDisplay'chip' | 'text' | 'chip' |
| How the chosen values are shown when several can be chosen: one dismissible chip each, or their labels joined by commas on a single line, cut short with an ellipsis. It changes nothing for a single value, which is always text. | ||
max | number | none |
How many chosen values to show before the rest are summed up as "+X", as chips or as text. It applies while the field is out of focus; focused, every value comes back so it can be seen and removed. Left out, or set to 0, every value is shown. It changes nothing without multiple. | ||
overflowText | (count: number) => string | none |
Rephrases the "+X" standing for the values beyond max, "+5 products" for instance. It receives the number of values being hidden. | ||
label | string | none |
| The label above the field, tied to it so that clicking it focuses the field. | ||
hint | string | none |
| A line of help under the field, read out along with the label. | ||
size | ComboboxSize'sm' | 'md' | 'lg' | 'md' |
| The height of the field: 32, 40 or 48 pixels. The panel and its rows follow it. | ||
compact | boolean | false |
| Takes 4px off the height, as everywhere else in the design system. | ||
placeholder | string | none |
| What the field says while nothing is chosen and nothing has been typed. | ||
disabled | boolean | false |
| Makes the field unusable, greyed out through the colour tokens. | ||
readonly | boolean | false |
Shows what has been chosen without letting it be changed: nothing can be typed, the list never opens, the chips lose their crosses and no clear cross is offered. The field keeps the focus and can be copied from, which is what separates it from disabled. | ||
invalid | boolean | false |
| Marks the field as invalid, for a rule of your own. | ||
iconStart | IconSource | none |
An icon inside the field, at the start. It is rendered before the chips rather than in their place. Decorative until a @click:icon-start listener turns it into a button. | ||
iconStartLabel | string | none |
| What the start icon does, in words, once it is clickable. | ||
expandIcon | IconSource | expand_more |
| The chevron at the end of the field, which turns as the list opens. Clicking it while the list is open closes the list. It is decoration all the same: the field itself opens the list and Escape closes it from the keyboard, so the chevron is hidden from screen readers and takes no label. | ||
clearable | boolean | false |
| Offers a cross that empties both the selection and the search. | ||
clearLabel | string | none |
| What that cross does, in words. It falls back to the design system dictionary. | ||
emptyText | string | none |
What the panel says when the search matches nothing. A screen reader hears it even when the #empty slot draws something else, so set both together. | ||
filter | ComboboxFilter | true |
| How the list is narrowed as one types. Turning it off means the options already arrive filtered by their source and are shown exactly as they come. A rule of your own receives the query as it was typed, merely trimmed, not the accent-insensitive form used internally. | ||
searchDebounce | number | 250 |
| How long to wait before telling the source what is being searched for, in milliseconds. Zero tells it at once, which suits a source that is not a network request. | ||
loading | boolean | false |
| Says that something is being loaded. With no option yet, the whole panel says so; with options already listed, a spinner appears at the foot of the list, since what is loading is then the next page. Either way the field replaces its chevron with a spinner. | ||
loadingText | string | none |
What is said while loading, and what the spinner is announced as. A screen reader hears it even when the #loading slot draws something else, so set both together. | ||
hasMore | boolean | false |
| Says that there are more pages to come, which is what makes the component ask for the next one as the end of the list comes into view. | ||
placement | ComboboxPlacement'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end' | 'bottom-start' |
| Where the list opens relative to the field. The panel is anchored in CSS, so this names a preference: a browser short of room already falls back on its own. | ||
v-model | ItemValue | ItemValue[] | '' |
The chosen option's value, or the list of them when multiple is set. It is an empty string to begin with, and the array is never mutated in place. | ||
Events
| Event | Type |
|---|---|
search | [query: string] |
What is being searched for, to be sent to the source. It is delayed by searchDebounce while typing, and emitted at once when the panel opens so that a first page can be loaded. The same term is never emitted twice in a row. | |
click:icon-start | [event: MouseEvent] |
The start icon was clicked. Attaching this listener is what turns that icon into a real button, which then needs iconStartLabel. | |
clear | [] |
| The clear cross emptied the selection and the search. | |
load-more | [] |
| The end of the list has come into view: send the next page. | |
Slots
| Slot | Type |
|---|---|
start | {} |
Content at the start of the field, rendered after iconStart rather than in its place. | |
value-end | {} |
Controls of your own inside the field, placed before the ones the field owns: the clear cross and the icon that opens the panel. Those two are the component own affordance, which is why there is no end slot here. | |
option | ComboboxOptionSlotProps |
| What a row of the list shows, in place of the plain label: a subtitle, an avatar, a badge. It is told whether the row is the highlighted one and whether it is already chosen. | |
chip | ComboboxChipSlotProps |
Replaces the chip standing for one chosen value. It receives remove, without which the value could no longer be taken back, and the size and density worked out to sit inside the field, which cannot be guessed from outside. The option itself may be missing, if that value has never appeared among the options. | |
overflow | ComboboxOverflowSlotProps |
Replaces the "+X" standing for the values beyond max. It receives count, the number of values being hidden, and the size and density of the chips inside the field, so that a chip of your own lines up with the others. | |
empty | ComboboxEmptySlotProps |
| What the panel shows when nothing matches. It receives the term that was searched. | |
loading | {} |
| What the panel shows while loading its first options. | |
Types
The types the tables above name, written as the library declares them. The ones carrying export can be imported from vectis-ui to type your own code; the others are the shape of what a slot hands out.
export interface BuiltinIcon {
name: string
paths: readonly [string] | readonly [string, string]
}export type ChipSize = 'xs' | 'sm'
export interface ComboboxChipSlotProps {
value: ItemValue
option: ComboboxOption | undefined
label: string
remove: () => void
size: ChipSize
compact: boolean
}export interface ComboboxEmptySlotProps {
query: string
}export type ComboboxFilter = boolean | ((option: ComboboxOption, query: string) => boolean)
export interface ComboboxGroup {
label: string
options: ComboboxOption[]
}export type ComboboxItem = ComboboxOption | ComboboxGroup | ComboboxSeparator
export interface ComboboxOption {
value: ItemValue
label: string
icon?: IconSource
disabled?: boolean
}export interface ComboboxOptionSlotProps {
option: ComboboxOption
index: number
active: boolean
selected: boolean
}export interface ComboboxOverflowSlotProps {
count: number
size: ChipSize
compact: boolean
}export interface ComboboxSeparator {
separator: true
}export type IconRender =
| { path: string; viewBox?: string }
| { component: Component; props?: Record<string, unknown> }
| { src: string }
| { text: string; class?: string }
| { class: string }export type IconSource = string | BuiltinIcon | IconRender
export type ItemValue = string | number
CSS variables
| Token | Value |
|---|---|
--vectis-control-size-combobox-list-max-block | 18rem |