Combobox

Matching uses fuse.js against each item's value and label, so small typos still match. Lower the trigger's threshold (default 0.28) to make matching stricter.

<script lang="ts">
    import * as Combobox from '@sivir-ui/svelte/components/combobox';

    const items = [
        { value: 'option-1', label: 'Option 1' },
        { value: 'option-2', label: 'Option 2' },
        { value: 'option-3', label: 'Option 3' }
    ];
</script>

<div class="w-[280px]">
    <Combobox.Root>
        <Combobox.Trigger placeholder="Select an option" class="w-full" />
        <Combobox.Content>
            <Combobox.Results>
                {#each items as item (item.value)}
                    <Combobox.Item value={item.value} label={item.label} callback={() => {}} />
                {/each}
            </Combobox.Results>
        </Combobox.Content>
    </Combobox.Root>
</div>

Installation

Copy the command below and run it in your terminal.

bunx @sivir-ui/svelte add combobox

Usage

Bind value on Combobox.Root. Every item needs a value and a label. The trigger is a text field that shows the selected label, so it takes a placeholder instead of children.

import * as Combobox from '@sivir-ui/svelte/components/combobox';

let framework = $state('');

<Combobox.Root bind:value={framework}>
  <Combobox.Trigger placeholder="Choose a framework" />
  <Combobox.Content>
    <Combobox.Results>
      <Combobox.Item value="sveltekit" label="SvelteKit" />
      <Combobox.Item value="astro" label="Astro" />
    </Combobox.Results>
  </Combobox.Content>
</Combobox.Root>

Examples

Basic usage

<script lang="ts">
    import * as Combobox from '@sivir-ui/svelte/components/combobox';

    const languages = [
        { value: 'en', label: 'English' },
        { value: 'es', label: 'Spanish' },
        { value: 'fr', label: 'French' },
        { value: 'de', label: 'German' },
        { value: 'ja', label: 'Japanese' },
        { value: 'pt', label: 'Portuguese' }
    ];
</script>

<div class="flex justify-center">
    <div class="w-[240px]">
        <Combobox.Root>
            <Combobox.Trigger placeholder="Select a language" class="w-full" />
            <Combobox.Content>
                <Combobox.Results>
                    {#each languages as item (item.value)}
                        <Combobox.Item value={item.value} label={item.label} callback={() => {}} />
                    {/each}
                </Combobox.Results>
            </Combobox.Content>
        </Combobox.Root>
    </div>
</div>

Scrollable

<script lang="ts">
    import * as Combobox from '@sivir-ui/svelte/components/combobox';

    const countries = [
        { value: 'ar', label: 'Argentina' },
        { value: 'au', label: 'Australia' },
        { value: 'at', label: 'Austria' },
        { value: 'be', label: 'Belgium' },
        { value: 'br', label: 'Brazil' },
        { value: 'ca', label: 'Canada' },
        { value: 'cl', label: 'Chile' },
        { value: 'cn', label: 'China' },
        { value: 'co', label: 'Colombia' },
        { value: 'cz', label: 'Czechia' },
        { value: 'dk', label: 'Denmark' },
        { value: 'eg', label: 'Egypt' },
        { value: 'fi', label: 'Finland' },
        { value: 'fr', label: 'France' },
        { value: 'de', label: 'Germany' },
        { value: 'gr', label: 'Greece' },
        { value: 'in', label: 'India' },
        { value: 'id', label: 'Indonesia' },
        { value: 'ie', label: 'Ireland' },
        { value: 'il', label: 'Israel' },
        { value: 'it', label: 'Italy' },
        { value: 'jp', label: 'Japan' },
        { value: 'ke', label: 'Kenya' },
        { value: 'kr', label: 'South Korea' },
        { value: 'mx', label: 'Mexico' },
        { value: 'nl', label: 'Netherlands' },
        { value: 'nz', label: 'New Zealand' },
        { value: 'ng', label: 'Nigeria' },
        { value: 'no', label: 'Norway' },
        { value: 'pl', label: 'Poland' },
        { value: 'pt', label: 'Portugal' },
        { value: 'sa', label: 'Saudi Arabia' },
        { value: 'sg', label: 'Singapore' },
        { value: 'za', label: 'South Africa' },
        { value: 'es', label: 'Spain' },
        { value: 'se', label: 'Sweden' },
        { value: 'ch', label: 'Switzerland' },
        { value: 'th', label: 'Thailand' },
        { value: 'tr', label: 'Turkey' },
        { value: 'ae', label: 'United Arab Emirates' },
        { value: 'gb', label: 'United Kingdom' },
        { value: 'us', label: 'United States' }
    ];
</script>

<div class="flex justify-center">
    <div class="w-[260px]">
        <Combobox.Root>
            <Combobox.Trigger placeholder="Select a country" class="w-full" />
            <Combobox.Content class="max-h-56">
                <Combobox.Results>
                    {#each countries as country (country.value)}
                        <Combobox.Item
                            value={country.value}
                            label={country.label}
                            callback={() => {}}
                        />
                    {/each}
                </Combobox.Results>
            </Combobox.Content>
        </Combobox.Root>
    </div>
</div>

Add a max-h-* class to Combobox.Content to cap the menu height. Longer result lists scroll inside it.

Anatomy

Combobox.Root - Holds the selected value and open state.

Combobox.Content - Renders the menu and, optionally, its search field.

Combobox.Trigger - Shows the selection and takes the search text.

Combobox.Results - Lists matching items.

Combobox.Item - Defines a selectable item.

Combobox.Label - Labels a group of items in the menu.