Combobox
Search local option labels while selected IDs stay separate from the query.
Usage
Selected: banana; changes: 0
<script lang="ts">
import { Combobox } from 'bref-ui';
let fruit = $state<string | undefined>();
const items = [{ id: 'apple', label: 'Apple' }];
</script>
<Combobox aria-label="Fruit" {items} bind:value={fruit} name="fruit" />
Playground
Live preview
Settings
View generated code
<script lang="ts">
import { Combobox } from 'bref-ui';
import type { SelectOptionDataProps } from 'bref-ui/types';
const items: SelectOptionDataProps[] = [
{
id: 'apple',
label: 'Apple',
icon: {
name: 'favorite',
filled: false
}
},
{
id: 'apricot',
label: 'Apricot'
},
{
id: 'banana',
label: 'Banana'
},
{
id: 'pear',
label: 'Pear',
disabled: true
}
];
let value = $state<string | string[] | undefined>(undefined);
let changes = $state(0);
</script>
<label for="playground-combobox">Choose fruit</label>
<Combobox
id="playground-combobox"
{items}
bind:value
size="medium"
disabled={false}
placeholder={'Select…'}
emptyMessage={{ message: 'No options found', icon: { name: 'search', filled: false } }}
wide={false}
variant="soft"
readonly={false}
onChange={() => changes++}
/>
<p role="status">Selected: {JSON.stringify(value) ?? 'none'}; changes: {changes}</p>
Sizes
Size applies to the input and options. Compact option rows sit in a contextual glass panel; the input keeps its opaque field fill.
States
An explicit aria-invalid attribute gives the visible input an error edge.
Multiple: none
Pear is disabled. The shared theme control switches light and dark modes.
Forms and keyboard
No submission yet. Current value: none
Typing filters labels. Arrows move the active option, Enter selects, and Escape or blur restores the selection. A composition Enter does not select. Form reset restores initial IDs. Required validation uses the hidden native form control; it does not automatically set aria-invalid on the visible input.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
items | readonly SelectOptionDataProps[] | Yes | — | Unique IDs, labels, optional icons and disabled state. |
value | string | string[] | No | undefined | Bindable selected ID or IDs; an array enables multiple selection. |
onChange | (value: string | string[]) => void | No | Omitted | Called when a user changes selection. |
id | string | No | Generated | Input ID for label association. |
placeholder | string | No | Select… | Text shown for an empty selection. |
emptyMessage | SelectEmptyProps | No | { message: 'No options found' } | Empty results text and optional decorative icon. |
size | BaseSize | No | medium | Control and option size. |
disabled | boolean | No | false | Disable the control and close the popup. |
variant | Exclude<Variant, 'filled'> | No | soft | Neutral or soft input appearance. |
wide | boolean | No | false | Fill the available input width. |
Supported native attributes: id, title, autocomplete, autofocus, tabindex, readonly, name, form, required . Use an external label or aria-label / aria-labelledby ; aria-describedby, aria-invalid, aria-errormessage are forwarded. Supported events: oninput, onkeydown, onblur, onfocus, onclick, oncompositionstart, oncompositionend . bind:value tracks selected IDs; typed text remains private. No snippets are exposed.
Long choices and scrolling
Long labels truncate within the panel. Arrow keys and Home or End move the active row into view. Selected rows keep a primary tick; hover and keyboard activity use separate tonal fills.