Skip to main content

Combobox

Combobox

Searchable select with async loading support

Tags: component form selection

View Demo

Combobox is a searchable dropdown that supports filtering options and async data loading.

Import​

<script>
  import { Combobox } from '@classic-homes/theme-svelte';
</script>

Basic Usage​

<script>
  let value = $state('');

  const options = [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'orange', label: 'Orange' },
  ];
</script>

<Combobox {options} bind:value placeholder="Select a fruit..." />

Props​

Prop Type Default Description
value string '' Current selected value (bindable)
options (ComboboxOption | SelectGroup)[] Required Array of options or grouped options
placeholder string 'Select an option...' Placeholder text
emptyMessage string 'No results found.' Message when no options match
disabled boolean false Disables the combobox
required boolean false Marks as required
name string - Name attribute for forms
id string - Element ID (auto-generated if empty)
label string - Label text
hint string - Hint text below the input
error string - Error message
loading boolean false Shows loading state
debounceMs number 300 Debounce time for search
onValueChange (value: string) => void - Callback when value changes
onSearch (query: string) => void - Callback for search queries
class string - Additional CSS classes

With Label and Hint​

<Combobox label="Select Country" hint="Start typing to search" options={countries} bind:value />

Searchable​

Users can type to filter options:

<Combobox options={countries} bind:value placeholder="Search countries..." />

Closed, the input shows the selected option's label. Opened, it clears so the full list shows, with the selected label as the placeholder.

Grouped Options​

Pass SelectGroups, the same shape Select takes. Typing filters within each group and hides groups with no match.

<Combobox
  label="Plan"
  options={[
    { label: 'Ranch', options: [{ value: 'aspen', label: 'Aspen' }] },
    { label: 'Two-story', options: [{ value: 'birch', label: 'Birch' }] },
  ]}
  bind:value
/>

Async Loading​

Load options based on search query:

<script>
  let value = $state('');
  let options = $state([]);
  let loading = $state(false);

  async function handleSearch(query: string) {
    loading = true;
    try {
      const response = await fetch(`/api/search?q=${query}`);
      options = await response.json();
    } finally {
      loading = false;
    }
  }
</script>

<Combobox
  label="Search"
  {options}
  bind:value
  {loading}
  onSearch={handleSearch}
  placeholder="Search..."
  emptyMessage="Type to search..."
/>

With Debounce​

Control the debounce timing:

<Combobox {options} bind:value onSearch={handleSearch} debounceMs={500} />

Validation​

Show validation errors:

<Combobox
  label="Country"
  {options}
  bind:value
  required
  error={!value ? 'Please select an option' : ''}
/>

Accessibility​

The Combobox component includes:

  • Full keyboard navigation
  • ARIA combobox pattern
  • Screen reader announcements
  • Focus management
  • Auto-generated IDs link labels to inputs
  • aria-describedby connects hint and error messages
  • aria-invalid set when error is present

Examples​

<script>
  let selectedUser = $state('');
  let users = $state([]);
  let loading = $state(false);

  async function searchUsers(query: string) {
    if (!query) {
      users = [];
      return;
    }
    loading = true;
    const response = await fetch(`/api/users?search=${query}`);
    users = await response.json();
    loading = false;
  }
</script>

<Combobox
  label="User"
  options={users}
  bind:value={selectedUser}
  onSearch={searchUsers}
  {loading}
  placeholder="Search users..."
  emptyMessage="No users found"
/>

Location Autocomplete​

<Combobox
  label="Location"
  hint="Enter a city name or zip code"
  options={locations}
  bind:value
  onSearch={searchLocations}
  placeholder="Enter city or zip code..."
  emptyMessage="No locations found"
/>
On this page