<script lang="ts">
import { Combobox } from 'kumo-svelte';
const fruits = [
'Apple',
'Apricot',
'Avocado',
'Banana',
'Blackberry',
'Blueberry',
'Cantaloupe',
'Cherry',
'Coconut',
'Cranberry',
'Date',
'Dragon Fruit',
'Fig',
'Grape',
'Grapefruit',
'Guava',
'Honeydew',
'Kiwi',
'Lemon',
'Lime',
'Lychee',
'Mango',
'Nectarine',
'Orange',
'Papaya',
'Passion Fruit',
'Peach',
'Pear',
'Pineapple',
'Plum',
'Raspberry',
'Strawberry',
'Watermelon'
];
let value = $state<string | null>('Apple');
</script>
<Combobox bind:value items={fruits}>
<Combobox.TriggerInput placeholder="Please select" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox> Installation
Barrel
import { Combobox } from 'kumo-svelte'; Granular
import { Combobox } from 'kumo-svelte/components/combobox';Usage
<script lang="ts"> import { Combobox } from 'kumo-svelte'; const fruits = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']; let value = $state<string | null>(null);</script><Combobox bind:value items={fruits}> <Combobox.TriggerInput placeholder="Select a fruit" /> <Combobox.Content> <Combobox.Empty /> <Combobox.List> {#snippet children(item)} <Combobox.Item value={item}>{item}</Combobox.Item> {/snippet} </Combobox.List> </Combobox.Content></Combobox>Examples
Sizes
The Combobox supports four size variants that match the Input component: xs, sm, base (default), and lg.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const fruits = [
'Apple',
'Apricot',
'Avocado',
'Banana',
'Blackberry',
'Blueberry',
'Cantaloupe',
'Cherry',
'Coconut',
'Cranberry',
'Date',
'Dragon Fruit',
'Fig',
'Grape',
'Grapefruit',
'Guava',
'Honeydew',
'Kiwi',
'Lemon',
'Lime',
'Lychee',
'Mango',
'Nectarine',
'Orange',
'Papaya',
'Passion Fruit',
'Peach',
'Pear',
'Pineapple',
'Plum',
'Raspberry',
'Strawberry',
'Watermelon'
];
</script>
<div class="flex flex-wrap items-center gap-4">
<Combobox size="sm" items={fruits.slice(0, 8)}>
<Combobox.TriggerInput placeholder="Small (sm)" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Combobox size="base" items={fruits.slice(0, 8)}>
<Combobox.TriggerInput placeholder="Base (default)" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div> Size also applies to TriggerValue (searchable inside variant):
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const languages = [
{ value: 'en', label: 'English', emoji: 'GB' },
{ value: 'fr', label: 'French', emoji: 'FR' }
];
</script>
<div class="flex flex-wrap items-center gap-4">
<Combobox size="sm" value={languages[0]} items={languages}>
<Combobox.TriggerValue class="w-[160px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search" />
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.emoji} {item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Combobox size="base" value={languages[1]} items={languages}>
<Combobox.TriggerValue class="w-[180px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search" />
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.emoji} {item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div> Searchable Item (Inside)
A searchable select component inside popup that allows users to filter and select.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const languages = [
{ value: 'en', label: 'English', emoji: 'GB' },
{ value: 'fr', label: 'French', emoji: 'FR' },
{ value: 'de', label: 'German', emoji: 'DE' },
{ value: 'es', label: 'Spanish', emoji: 'ES' }
];
let value = $state<any>(languages[0]);
</script>
<Combobox bind:value items={languages}>
<Combobox.TriggerValue class="w-[200px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search languages" />
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.emoji} {item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox> Searchable Select with Placeholder
Use TriggerValue with a placeholder prop to create a searchable Select-style field.
The placeholder is displayed until a value is selected.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const languages = [
{ value: 'en', label: 'English', emoji: 'GB' },
{ value: 'fr', label: 'French', emoji: 'FR' },
{ value: 'de', label: 'German', emoji: 'DE' },
{ value: 'es', label: 'Spanish', emoji: 'ES' }
];
let value = $state<any>(null);
</script>
<Combobox bind:value items={languages}>
<Combobox.TriggerValue class="w-[200px]" placeholder="Select a language" />
<Combobox.Content>
<Combobox.Input placeholder="Search languages" />
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.emoji} {item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox> Custom Trigger
Use Combobox.Trigger with a render prop to replace the default input-like trigger with your own element. Pair with Combobox.Value to display the selected value. Useful for account switchers, sidebar navigation, or anywhere the default chrome doesn't fit.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
import { MagnifyingGlass } from 'phosphor-svelte';
const languages = [
{ value: 'en', label: 'English', emoji: 'GB' },
{ value: 'fr', label: 'French', emoji: 'FR' }
];
let value = $state(languages[0]);
</script>
<Combobox bind:value items={languages}>
<Combobox.Trigger class="rounded-md px-2 py-1 text-sm hover:bg-kumo-fill-hover">
<Combobox.Value>
<span class="truncate">{value.emoji} {value.label}</span>
</Combobox.Value>
<MagnifyingGlass class="size-3.5 shrink-0 text-kumo-subtle" />
</Combobox.Trigger>
<Combobox.Content>
<Combobox.Input placeholder="Search languages" />
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.emoji} {item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox> Grouped
Group items into categories using the Group and GroupLabel components.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const servers = [
{ value: 'Asia', items: [{ label: 'Japan', value: 'japan' }, { label: 'Singapore', value: 'singapore' }] },
{ value: 'Europe', items: [{ label: 'Germany', value: 'germany' }, { label: 'France', value: 'france' }] }
];
</script>
<Combobox items={servers}>
<Combobox.TriggerInput class="w-[200px]" placeholder="Select server" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(group)}
<Combobox.Group items={group.items}>
<Combobox.GroupLabel>{group.value}</Combobox.GroupLabel>
<Combobox.Collection>
{#snippet children(item)}
<Combobox.Item value={item}>{item.label}</Combobox.Item>
{/snippet}
</Combobox.Collection>
</Combobox.Group>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox> Multiple
Allow users to select multiple options from the list.
<script lang="ts">
import { Button, Combobox, Text } from 'kumo-svelte';
const bots = [
{ value: 'googlebot', label: 'Googlebot', author: 'Google' },
{ value: 'bingbot', label: 'Bingbot', author: 'Microsoft' }
];
let value = $state<any[]>([]);
</script>
<div class="flex gap-2">
<Combobox bind:value items={bots} multiple>
<Combobox.TriggerMultipleWithInput class="w-[400px]" placeholder="Select bots">
{#snippet children(selected)}
<Combobox.Chip value={selected}>{selected.label}</Combobox.Chip>
{/snippet}
</Combobox.TriggerMultipleWithInput>
<Combobox.Content class="max-h-[200px] min-w-auto overflow-y-auto">
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>
<div class="flex gap-2">
<Text>{item.label}</Text>
<Text variant="secondary">{item.author}</Text>
</div>
</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Button variant="primary">Submit</Button>
</div> With Field
Add label and description using the built-in Field wrapper.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const databases = [
{ value: 'postgres', label: 'PostgreSQL' },
{ value: 'mysql', label: 'MySQL' },
{ value: 'redis', label: 'Redis' }
];
</script>
<div class="w-80">
<Combobox
items={databases}
label="Database"
description="Select your preferred database"
>
<Combobox.TriggerInput placeholder="Select database" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div> Disabled
Pass the disabled prop to prevent interaction. Works with both TriggerInput and TriggerValue.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const fruits = [
'Apple',
'Apricot',
'Avocado',
'Banana',
'Blackberry',
'Blueberry',
'Cantaloupe',
'Cherry',
'Coconut',
'Cranberry',
'Date',
'Dragon Fruit',
'Fig',
'Grape',
'Grapefruit',
'Guava',
'Honeydew',
'Kiwi',
'Lemon',
'Lime',
'Lychee',
'Mango',
'Nectarine',
'Orange',
'Papaya',
'Passion Fruit',
'Peach',
'Pear',
'Pineapple',
'Plum',
'Raspberry',
'Strawberry',
'Watermelon'
];
const languages = [
{ value: 'en', label: 'English', emoji: 'GB' },
{ value: 'fr', label: 'French', emoji: 'FR' }
];
</script>
<div class="flex flex-wrap items-start gap-4">
<Combobox value="Apple" items={fruits} disabled>
<Combobox.TriggerInput class="w-[200px]" placeholder="Select fruit" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Combobox value={languages[0]} items={languages} disabled>
<Combobox.TriggerValue class="w-[200px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search" />
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.emoji} {item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div> Disabled Items
Pass the disabled prop to an individual Combobox.Item to make it non-selectable. Disabled rows are rendered with a muted style and skipped during keyboard navigation selection.
<script lang="ts">
import { Combobox, Text } from 'kumo-svelte';
const items = [
{ value: 'postgres', label: 'PostgreSQL' },
{ value: 'mysql', label: 'MySQL' },
{ value: 'mariadb', label: 'MariaDB', disabled: true, reason: 'Beta' },
{ value: 'mongodb', label: 'MongoDB' },
{ value: 'cassandra', label: 'Apache Cassandra', disabled: true, reason: 'Coming soon' }
];
</script>
<div class="w-80">
<Combobox items={items}>
<Combobox.TriggerInput placeholder="Select database" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item} disabled={item.disabled}>
<span>
{item.label}
{#if item.reason}
<Text variant="secondary" size="xs" as="span"> - {item.reason}</Text>
{/if}
</span>
</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div> Error State
Display validation errors with the error prop.
<script lang="ts">
import { Combobox } from 'kumo-svelte';
const databases = [
{ value: 'postgres', label: 'PostgreSQL' },
{ value: 'mysql', label: 'MySQL' },
{ value: 'redis', label: 'Redis' }
];
</script>
<div class="w-80">
<Combobox
items={databases}
label="Database"
error={{ message: "Please select a database", match: true }}
>
<Combobox.TriggerInput placeholder="Select database" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{#snippet children(item)}
<Combobox.Item value={item}>{item.label}</Combobox.Item>
{/snippet}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div> Filtering
Filtering is case- and accent-insensitive by default, powered by Intl.Collator under the hood. For string items, no custom filter is needed.
When filtering on a property of object items, use Combobox.useFilter() to
preserve the built-in accent-insensitive matching:
<script lang="ts"> import { Combobox } from "kumo-svelte"; const { contains } = Combobox.useFilter(); const languages = [ { value: "pt", label: "Portuguese", emoji: "🇵🇹" }, { value: "es", label: "Spanish", emoji: "🇪🇸" } ]; const filter = (item, query) => contains(item.label, query);</script><Combobox items={languages} {filter}> <!-- ... --></Combobox> To disable filtering entirely (for example, when results come from a server),
pass filter={null}:
<Combobox items={results} filter={null}> <!-- ... --></Combobox>Customizing Dropdown Height
By default, Combobox.Content has a max height of 24rem (384px) or the available viewport space, whichever is smaller. The dropdown scrolls automatically when content exceeds this height.
To customize the max height, pass a class to Combobox.Content:
// Shorter dropdown (200px)<Combobox.Content class="max-h-[200px]">// Taller dropdown (500px)<Combobox.Content class="max-h-[500px]">// Use Tailwind presets<Combobox.Content class="max-h-64"> // 256px<Combobox.Content class="max-h-96"> // 384px (same as default)API Reference
Combobox
Root component for the searchable select.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | 'xs' | 'sm' | 'base' | 'lg' | "base" | Size preset. |
| items * | unknown[] | - | Array of items to display in the dropdown. |
| value | unknown | - | Controlled value. |
| children | Snippet | - | Child snippet rendered by the component. |
| class | string | - | Additional classes merged onto the root element. |
| label | string | Snippet | - | Visible label content. |
| required | boolean | - | Marks the field as required. |
| labelTooltip | string | Snippet | - | Optional help content for the label. |
| description | string | Snippet | - | Supporting description text. |
| error | FieldError | - | Validation error message or matcher. |
| onValueChange | (value: unknown) => void | - | Called when the value changes. |
| multiple | boolean | false | Enables multiple selection. |
| onOpenChange | (open: boolean) => void | - | Called when open state changes. |
Combobox.Content
Dropdown container for the list.
| Prop | Type | Default | Description |
|---|---|---|---|
| align | 'start' | 'center' | 'end' | "start" | Alignment of the popup relative to the trigger. |
| alignOffset | number | string | - | Offset along the alignment axis. |
| side | 'top' | 'right' | 'bottom' | 'left' | "bottom" | Side of the trigger where the popup is placed. |
| sideOffset | number | string | 4 | Offset between the popup and the trigger. |
Combobox.Item
Individual selectable option.
| Prop | Type | Default | Description |
|---|---|---|---|
| value * | ComboboxItem | - | Controlled value. |
| disabled | boolean | - | Disables the component. |
Additional Sub-components
- `Combobox.TriggerInput` - Single-select input trigger
- `Combobox.TriggerValue` - Button trigger showing selected value
- `Combobox.TriggerMultipleWithInput` - Multi-select with chips
- `Combobox.Input` - Search input inside dropdown
- `Combobox.List` - List container with render prop
- `Combobox.Group` - Group container for categorized items
- `Combobox.GroupLabel` - Header label for a group
- `Combobox.Collection` - Items container within a group
- `Combobox.Chip` - Selected item chip
- `Combobox.Empty` - Empty state message