Components · Combobox · Use
Combobox
Option data, grouping, and selection cardinality belong to the application; this recipe has one visual construction.
Svelte
React
Vue
State
Markup
<script lang="ts">
import { Combobox, createListCollection } from '@ark-ui/svelte';
import { useFilter } from '@ark-ui/svelte/locale';
import CheckIcon from '@lucide/svelte/icons/check';
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
const frameworks = createListCollection({
items: [
{ label: 'Svelte', value: 'svelte' },
{ label: 'React', value: 'react' },
{ label: 'Vue', value: 'vue' }
]
});
const filter = useFilter({ sensitivity: 'base' });
let query = $state('');
// Filtering through the collection's own `filter` keeps Ark's keyboard
// navigation in step with what is rendered.
const filtered = $derived(
query.trim() ? frameworks.filter((item) => filter().contains(item, query)) : frameworks
);
</script>
<Combobox.Root
collection={filtered}
inputValue={query}
onInputValueChange={(details) => (query = details.inputValue)}
class="combobox-root"
>
<Combobox.Label class="combobox-label">Framework</Combobox.Label>
<Combobox.Control class="combobox-control">
<Combobox.Input class="combobox-input" placeholder="Choose or type…" />
<Combobox.Trigger class="combobox-trigger" aria-label="Open options">
<ChevronDownIcon />
</Combobox.Trigger>
</Combobox.Control>
<Combobox.Positioner class="combobox-positioner">
<Combobox.Content class="combobox-content">
<Combobox.Empty class="combobox-empty">No matches.</Combobox.Empty>
<Combobox.List class="combobox-list">
{#each filtered.items as option (option.value)}
<Combobox.Item item={option} class="combobox-item">
<Combobox.ItemText class="combobox-item-text">{option.label}</Combobox.ItemText>
<Combobox.ItemIndicator class="combobox-item-indicator">
<CheckIcon />
</Combobox.ItemIndicator>
</Combobox.Item>
{/each}
</Combobox.List>
</Combobox.Content>
</Combobox.Positioner>
</Combobox.Root>