Skip to main content

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

open
boolean
Controls whether the combobox dropdown is open
value
unknown
The selected value (single-select mode)
values
unknown[]
Array of selected values (multi-select mode)
label
string
Label for the selected item
labels
string[]
Labels for selected items
multiple
boolean
default:"false"
Enable multi-select mode
disabled
boolean
default:"false"
Disable the combobox
placement
string
default:"bottom-start"
Popover placement position
placements
string[]
Allowed placement positions
offset
number
default:"1"
Offset distance from the trigger
factory
Factory<ComboboxBond>
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
string
Placeholder text for the input

Combobox.Item Props

value
string
required
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

Build docs developers (and LLMs) love