Combobox ported
A searchable choice field that filters without leaving the keyboard.
A labelled text field with a filterable listbox. Typing narrows the list by label or keywords, arrows move the highlight, and Enter picks. Built on Bits UI Combobox for the list semantics with ARC styling verbatim.
Loading demo…
Guidance
When to use
- Long or searchable lists where typing narrows the choices, such as countries or repositories.
- Form fields that submit the chosen value through the text input name.
- Single-choice selectors that need full keyboard navigation and an empty state.
When not to use
- Use select for short fixed lists where typing is not needed.
- Use multi-select when several values can be chosen.
- Use chip-group when the choices should stay visible as toggleable filters.
pnpm add sveltearcUsage
<script lang="ts">
import { Combobox } from '$lib/components/combobox';
let fruit = $state('apple');
const options = [
{ value: 'apple', label: 'Apple', keywords: ['fruit', 'red'] },
{ value: 'banana', label: 'Banana', keywords: ['fruit', 'yellow'] },
{ value: 'cherry', label: 'Cherry', disabled: true }
];
</script>
<Combobox
label="Fruit"
bind:value={fruit}
{options}
description="Pick the fruit for this week's box."
/>Variants & Examples
Controlled with Description
Two-way binding via bind:value with helper text linked through aria-describedby.
<Combobox
label="Fruit"
bind:value={fruit}
options={fruits}
description="Linked helper text for assistive technology."
/>Open State and Empty Message
Control the listbox visibility with bind:open and customise the no-match copy.
<Combobox
label="City"
bind:value={city}
bind:open={menuOpen}
options={cities}
emptyMessage="No cities match that search"
/>Disabled
Disables the whole field, or individual options via disabled on the option.
<Combobox
label="Restricted Field"
value="apple"
disabled
options={fruits}
/>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | – | Visible label for the field, linked to the input via for attribute. |
options | ComboboxOption[] | – | Array of { value: string; label: string; disabled?: boolean; keywords?: string[] } items in list order. |
value | string | undefined | Controlled selected value; supports two-way bind:value. |
defaultValue | string | '' | Initial value when uncontrolled. |
onValueChange | (value: string) => void | – | Callback fired when an option is chosen or the selection is cleared. |
open | boolean | undefined | Controlled open state of the listbox; supports two-way bind:open. |
onOpenChange | (open: boolean) => void | – | Callback fired when the listbox opens or closes. |
description | string | – | Helper copy rendered below the field, linked through aria-describedby. |
placeholder | string | 'Search or select…' | Placeholder copy shown when nothing is selected. |
emptyMessage | string | 'No matches found' | Copy shown when the filter matches no option. |
id | string | – | Input ID; auto-generated via $props.id() when omitted. |
disabled | boolean | false | Whether the field is disabled. |
class | string | – | Additional CSS class applied to the control element. |
ref | HTMLInputElement | null | – | Bindable reference to the underlying text field. |
Keyboard Interactions
| Key | Action |
|---|---|
ArrowDown / ArrowUp | Opens the list from the field, then moves the highlight between enabled options. |
Enter | Chooses the highlighted option and closes the list. |
Escape | Closes the list without changing the selected value. |
Type to filter | Narrows the list by label or keywords as you type. |
Accessibility
- Input carries role="combobox" with aria-expanded, aria-controls, aria-autocomplete="list", and aria-activedescendant tracking the highlight.
- List semantics (role="listbox", role="option", aria-selected) come from Bits UI Combobox parts.
- Label is linked with for/id, and description through aria-describedby.
- Disabled options are exposed with aria-disabled and skipped by keyboard navigation.
Motion
Phase 1 still-state: the list mounts when open at its resting end-state with no enter/exit travel; the clear button renders statically; the chosen label swaps without the rise-in/blur settle. Phase 2 wires the popover spring, list auto-height morph, clear-button scale/blur, and the label settle animation. Reduced motion keeps the same end-states with instant transitions.
Notes for AI
- Pick for long or searchable single-choice lists where typing narrows options.
- Two-way binding is available via bind:value={selected} and bind:open={menuOpen}.
- Use options with value, label, optional disabled, and optional keywords for filter aliases.
Source of truth: registry/components/combobox/combobox.tsx