Overview
The Combobox component combines a text input with a dropdown menu for selection. It’s ideal for searchable select inputs where users can type to filter options.
Installation
import { Combobox } from '@svelte-atoms/core';
import { filterDropdownData } from '@svelte-atoms/core';
Basic Usage
<script>
import { Combobox, filterDropdownData } from '@svelte-atoms/core';
import { Input } from '@svelte-atoms/core';
const currencies = [
{ value: 'usd', label: 'US Dollar' },
{ value: 'eur', label: 'Euro' },
{ value: 'gbp', label: 'British Pound' },
{ value: 'jpy', label: 'Japanese Yen' }
];
const filtered = filterDropdownData(
() => currencies,
(query, item) => item.label.toLowerCase().includes(query.toLowerCase())
);
</script>
<Combobox.Root>
<Combobox.Trigger base={Input.Root} class="min-w-sm">
<Combobox.Control placeholder="Select currency" />
</Combobox.Trigger>
<Combobox.Content>
<input
bind:value={filtered.query}
class="border-b border-border px-4 py-3"
placeholder="Type to filter..."
/>
{#each filtered.current as item (item.value)}
<Combobox.Item value={item.value}>{item.label}</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Root>
Combobox.Root Props
Controls whether the combobox dropdown is open
The selected value (single-select mode)
Array of selected values (multi-select mode)
Label for the selected item
Labels for selected items
placement
string
default:"bottom-start"
Popover placement position
Allowed placement positions
Offset distance from the trigger
Custom factory function for creating combobox bond
Combobox.Trigger Props
Extends PopoverTrigger props and all HtmlAtomProps.
Combobox.Control Props
Extends InputControl props. This is the text input element within the combobox.
Placeholder text for the input
Combobox.Item Props
Unique identifier for this item
Extends Dropdown.Item props.
Combobox.Selections Props
Extends Dropdown.Selections props. Displays selected items in multi-select mode.
Combobox.Content Props
Extends Dropdown.Content props. The popover content container.
Subcomponents
Combobox.Root
Root container managing combobox state.
Combobox.Trigger
Trigger container that includes the input field.
Combobox.Control
The text input field for typing and filtering.
Combobox.Content
Popover content container for combobox items.
Combobox.Item
Individual selectable item in the dropdown.
Combobox.Selections
Container displaying selected items (multi-select mode).
Combobox.Selection
Individual selected item with remove button.
Combobox.Placeholder
Placeholder text when no items are selected.
Combobox.Indicator
Visual indicator for the combobox state.
Combobox.Arrow
Arrow pointing to the trigger.
Combobox.List
Scrollable list container for items.
Combobox.Group
Group container for related items.
Combobox.Title
Title for a group of items.
Combobox.Divider
Visual divider between items or groups.
Examples
Multi-select with Inline Input
<script>
import { Combobox, filterDropdownData } from '@svelte-atoms/core';
import { Input } from '@svelte-atoms/core';
import { Divider } from '@svelte-atoms/core';
let selectedLabels = $state([]);
const currencies = [
{ value: 'usd', label: 'US Dollar' },
{ value: 'eur', label: 'Euro' },
{ value: 'gbp', label: 'British Pound' },
{ value: 'jpy', label: 'Japanese Yen' },
{ value: 'cny', label: 'Chinese Yuan' }
];
const filtered = filterDropdownData(
() => currencies,
(query, item) => item.label.toLowerCase().includes(query.toLowerCase())
);
</script>
<Combobox.Root bind:labels={selectedLabels} multiple>
<Combobox.Trigger
base={Input.Root}
class="flex h-auto min-h-10 min-w-sm flex-col items-start gap-2"
>
<div class="flex">
<Input.Icon class="text-foreground/50">$</Input.Icon>
<Divider class="mx-1" vertical />
<Combobox.Control class="px-1" placeholder="Select currencies" />
</div>
<Combobox.Selections />
</Combobox.Trigger>
<Combobox.Content>
<input
bind:value={filtered.query}
class="border-b border-border px-4 py-3"
placeholder="Type to filter..."
/>
{#each filtered.current as item (item.value)}
<Combobox.Item value={item.value}>{item.label}</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Root>
Single Select with Custom Control
<script>
import { Combobox } from '@svelte-atoms/core';
import { Input } from '@svelte-atoms/core';
let value = $state('');
const options = [
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'angular', label: 'Angular' }
];
</script>
<Combobox.Root bind:value>
<Combobox.Trigger base={Input.Root}>
<Combobox.Control placeholder="Choose a framework" />
</Combobox.Trigger>
<Combobox.Content>
{#each options as option (option.value)}
<Combobox.Item value={option.value}>{option.label}</Combobox.Item>
{/each}
</Combobox.Content>
</Combobox.Root>
With Groups
<Combobox.Root>
<Combobox.Trigger base={Input.Root}>
<Combobox.Control placeholder="Select option" />
</Combobox.Trigger>
<Combobox.Content>
<Combobox.Group>
<Combobox.Title>Fruits</Combobox.Title>
<Combobox.Item value="apple">Apple</Combobox.Item>
<Combobox.Item value="banana">Banana</Combobox.Item>
</Combobox.Group>
<Combobox.Divider />
<Combobox.Group>
<Combobox.Title>Vegetables</Combobox.Title>
<Combobox.Item value="carrot">Carrot</Combobox.Item>
<Combobox.Item value="lettuce">Lettuce</Combobox.Item>
</Combobox.Group>
</Combobox.Content>
</Combobox.Root>
Filtering Utility
Use the filterDropdownData utility to create reactive filtered lists:
<script>
import { filterDropdownData } from '@svelte-atoms/core';
const data = $state([...]);
const filtered = filterDropdownData(
() => data,
(query, item) => {
// Custom filter logic
return item.label.toLowerCase().includes(query.toLowerCase());
}
);
</script>
Accessibility
- Keyboard navigation with arrow keys
- Enter/Space to select items
- Escape to close dropdown
- Screen reader support with ARIA attributes
- Focus management between input and dropdown
- Input field is always accessible for typing