Combobox
Installation
npx @mielui/svelte add comboboxyarn dlx @mielui/svelte add comboboxbunx @mielui/svelte add comboboxUsage
Bind value to the selected option. Single selection is the default. Clicking an option or pressing Enter updates value and calls onValueChange. Keyboard navigation skips disabled options. Changing an item label updates the selected label without replacing an active search.
Set name on Trigger to submit the selected value. Search text and labels are not submitted. Trigger renders an input by default, or a button with searchPlacement="menu". Its bind:element matches that element type.
An item callback runs when that option changes the selection. Selecting the current option again in single mode closes the menu without reporting a value change. Call preventDefault() in Trigger's onclick to cancel click activation. Input appearance can still open on focus or typing. Trigger's disabled prop disables both editing and its clear action.
Set type="multiple" on Root and bind a string array to value to choose several options. Selecting an item
toggles it, clears the search, and keeps the menu open. The trigger lists selected
labels; Escape or an outside click closes the menu. With name on Trigger, each selected
value submits under that name.
<script lang="ts">
import * as Combobox from '@mielui/svelte/components/combobox';
let framework = $state('sveltekit');
</script>
<Combobox.Root bind:value={framework}>
<Combobox.Trigger placeholder="Select a framework" />
<Combobox.Content>
<Combobox.Results>
<Combobox.Item value="nextjs" label="Next.js" />
<Combobox.Item value="sveltekit" label="SvelteKit" />
</Combobox.Results>
</Combobox.Content>
</Combobox.Root>Examples
Basic usage
Multiple selection
Search for teams and toggle each selection without reopening the menu.
1 team selected
Input search
Set appearance="input" for a field-styled trigger that stays editable and opens on focus or typing instead
of click-toggle. Pass a trailing snippet for an adornment; there is no chevron by default.
Search in the menu
Keep the trigger select-like and place the search field in the menu.
Scrollable
Long result lists stay in a height-capped menu and scroll inside it.
Glass surface
Set surface="glass" on Content for a translucent panel with blur, or surface="solid" for an opaque one. Without surface, the panel follows the theme. Glass falls back to an opaque panel when backdrop filtering is unavailable or reduced transparency is on.
Custom item content
Item renders its label by default. Pass children to show an icon or a second line instead. The label is still required: search matches against it and the trigger shows it once the item is selected.
<Combobox.Item value="fr" label="France">
<span>France</span>
<span class="text-foreground-muted">Europe</span>
</Combobox.Item>Labels
Built-in text is English. Pass a ComboboxLabels object to labels on Root to translate or reword the menu search placeholder and name, the clear button, the option list name and the empty message. Every key is optional, and an omitted key keeps its English default. The trigger placeholder is a prop on Trigger.
<script lang="ts">
import * as Combobox from '@mielui/svelte/components/combobox';
import type { ComboboxLabels } from '@mielui/svelte/components/combobox';
const labels: ComboboxLabels = {
searchPlaceholder: 'Rechercher…',
search: 'Rechercher un pays',
clear: 'Effacer',
options: 'Pays',
empty: 'Aucun résultat'
};
</script>
<Combobox.Root {labels}>
<Combobox.Trigger placeholder="Choisir un pays" searchPlacement="menu" />
<Combobox.Content>
<Combobox.Results>
<Combobox.Item value="fr" label="France" />
<Combobox.Item value="be" label="Belgique" />
</Combobox.Results>
</Combobox.Content>
</Combobox.Root>Matching typed text
The combobox filters with fuzzy matching, so a small typo still finds the option. threshold on Trigger sets how forgiving it is, from 0 for an exact match to 1 for nearly anything. The default is 0.28. Lower it for lists of codes or ids where a near miss is a wrong answer.
Opening and placement
placement on Root picks the side and alignment of the list, such as top-start. It defaults to bottom and flips when there is no room. onOpenChange runs when the person opens or closes the list, and onopen on Trigger runs just before it opens, which is the place to fetch options.
hoverable opens the list when the pointer rests on the trigger, with delay and closeDelay in milliseconds. inert is on by default and makes the rest of the page inert while a click-opened list is showing.
Options requested 0 times
Panel and items
Content shares its panel with Popover. allowClickOutside={false} keeps the list open on an outside press, and dismissLayer={false} removes the invisible layer that catches the first outside press, which the input-style trigger relies on to stay typeable. lockScroll decides whether page scrolling is held while it is open; Tab is never trapped, so it moves on from the list. portal={false} renders the list in place, refElement anchors it to another element, and role and tabindex set the panel's ARIA role and tab index. surfaceClass styles the inner surface while class styles the frame.
Item is built on Button and always uses the menu row style, so Button's variant, size, and unstyled do not apply. A row is always a button and does not take href. status or loading show progress on a row, with labels setting the text for each state, and onkeydown runs before the list handles the key. Trigger takes Button's size, style, and unstyled.
API reference
Combobox.Root
Provides combobox state.
childrenSnippet<[]>Content rendered inside.
openBindablebooleanWhether it is open. Bind it to control the state from outside.
Default
falseonOpenChange((open: boolean) => void)Called with the new state whenever it opens or closes.
placementPlacementPreferred side and alignment relative to the trigger, such as
bottom-start. It flips when there is no room.Default
'bottom'hoverablebooleanOpens on hover as well as on click.
Default
falsedelaynumberMilliseconds the pointer rests on the trigger before it opens.
Default
0closeDelaynumberMilliseconds after the pointer leaves before it closes.
Default
150inertbooleanMake document content outside an open non-hover popover inert. Defaults to
true.Default
truelabelsComboboxLabelsOverrides the built-in text and accessible names. Every key is optional and English is the fallback.
type"single" | "multiple"singlekeeps one value.multipleallows several and makesvaluean array.valueBindablestring | string[]Selected value, or an array in multiple mode. Bindable.
Default
undefinedonValueChange((value: string) => void) | ((value: string[]) => void)Called with the new value when it changes.
Combobox.Content
Contains combobox controls.
childrenRequiredSnippet<[]>Content rendered inside.
class(string & (ClassValue | null))Classes added to the element.
tabindexnumberTab index of the panel.
Default
-1role"dialog" | "alertdialog" | "menu" | "listbox" | "none"ARIA role of the panel.
Default
'none'surface"solid" | "glass"Surface treatment. Omit it to inherit
--mielui-surfacefrom the theme.surfaceClassstringClasses for the inset surface (where children live) — padding, layout, background overrides. The
classprop styles the outer Panel frame.allowClickOutsidebooleanCloses when the pointer is pressed outside it. Set false to keep it open until it is dismissed another way.
Default
truedismissLayerbooleanRender the full-viewport dismiss layer under the panel while open. Defaults to
true. Set tofalsefor triggers that must stay clickable while open (e.g. an input-style combobox trigger); outside pointer dismissal still applies viaallowClickOutside.Default
trueportalbooleanMoves the panel to the end of the document so ancestors cannot clip it. Set false to render it in place.
Default
truerefElementVirtualElementPositions the panel against this element or virtual element instead of the trigger. The panel then opens to the right of it, as a submenu does.
lockScrollbooleanLock document scrolling while the panel is open. Defaults to
true.Default
true
Combobox.Item
Defines a selectable item.
valueRequiredstring | (string & string[])Value this option selects.
labelRequiredstringText used for search and for the trigger, when it differs from the option's content.
callback(() => void)Called when the item is chosen with the pointer or the keyboard.
childrenSnippet<[]>Content rendered inside.
onkeydown((event: KeyboardEvent) => void)Called on key down, before the button handles the key itself.
onclick((event: MouseEvent) => void)Called when the button is activated. Loading and disabled buttons do not call it.
statusButtonStatusControlled visual state. Loading remains focusable and refuses activation.
disabledbooleanPrevents activation. A disabled link loses its destination and is skipped by Tab.
Default
falseelementBindableHTMLButtonElement | HTMLAnchorElementBindable reference to the rendered DOM element. Type is the union of the two possible element types -- narrow at the use site:
loadingbooleanConvenience alias for
status="loading".labelsButtonLabelsOverrides the built-in text and accessible names. Every key is optional and English is the fallback.
Combobox.Label
Labels the combobox.
childrenRequiredSnippet<[]>Content rendered inside.
classstringClasses added to the element.
Combobox.Results
Lists matching items.
childrenSnippet<[]>Content rendered inside.
Combobox.Trigger
Opens the combobox.
classstringClasses added to the element.
stylestringInline styles for the element.
onclick((event: MouseEvent) => void)Called when it is clicked or activated.
variantButtonVariantButton style of the trigger.
Default
'outline'size"sm" | "md" | "lg" | "icon"Control height of the trigger.
Default
'md'unstyledbooleanForwarded to Button: render with
classalone, skipping variant/size.iconbooleanShows the chevron after the label.
onopen(() => void)Called immediately before this trigger opens its popover.
aria-labelledbystringId of the element that names the control, such as a visible label.
trailingSnippet<[]>Content at the end of the trigger.
placeholderstringText shown while there is no value.
Default
'Select…'searchPlacement"trigger" | "menu"triggertypes into the trigger.menuputs a search field inside the menu.Default
'trigger'thresholdnumberHow loosely typed text may match, from 0 for exact to 1 for anything.
Default
0.28appearance"button" | "input"buttonlooks like a select.inputlooks like a text field.Default
'button'elementBindableHTMLButtonElement | HTMLInputElementBindable reference to the DOM element.
pnpm dlx @mielui/svelte add combobox