Skip to main content
Urbicon UI

Combobox

Searchable autocomplete input with keyboard navigation and custom filtering.

Playground

Variant Style variant (tailwind-variants)
Tier Style variant (tailwind-variants)
<script lang="ts">
  import { Combobox } from '@urbicon-ui/blocks';

  const options = [
    { label: 'United States', value: 'us' },
    { label: 'United Kingdom', value: 'uk' },
    { label: 'Germany', value: 'de' },
    { label: 'France', value: 'fr' },
    { label: 'Japan', value: 'jp' },
    { label: 'Australia', value: 'au' },
    { label: 'Canada', value: 'ca' },
    { label: 'Brazil', value: 'br' }
  ];
</script>

<Combobox
  {options}
  placeholder="Search countries…"
/>

01 Examples

Multi-select with tags

Pass multiple to bind an array of values. Picks render as removable tag chips below the search input, the listbox stays open across selections, Backspace on an empty field removes the last tag, and maxItems caps the count — non-selected options grey out once the cap is reached.
TypeScript Svelte
<Combobox
  label="Skills"
  options={languages}
  multiple
  bind:value={skillsValue}
  maxItems={5}
  placeholder="Add skills…"
  clearable
/>

Helper, error & required

Combobox follows the same form-field contract as Input and Select — label, helper, error, and required all work as expected. error overrides helper when both are set.
We use this to schedule meetings
<Combobox
  label="Timezone"
  options={timezones}
  bind:value={timezoneValue}
  placeholder="Search…"
  helper="We use this to schedule meetings"
  required
  clearable
/>
<Combobox
  label="Primary language"
  options={languages}
  error="Please select your primary language"
  placeholder="Search…"
/>

Custom filter

Replace the default case-insensitive contains-match with your own predicate — here, strict startsWith matching for command-style input.
<Combobox
  label="Language"
  options={languages}
  bind:value={filterValue}
  placeholder="Type to match…"
  filter={(opt: ComboboxOption, q: string) =>
    opt.label.toLowerCase().startsWith(q.toLowerCase())}
/>

Custom option renderer

Use the customOption snippet for rich list items — avatars, badges, secondary descriptions, status indicators.
<Combobox
  label="Team member"
  options={teamMembers}
  bind:value={customValue}
  placeholder="Search team…"
  clearable
>
  {#snippet customOption(opt: ComboboxOption, isSelected: boolean)}
    <div class="flex w-full items-center gap-3">
      <Avatar src={avatars[opt.value]} size="xs" />
      <div class="flex flex-1 items-center gap-2 truncate">
        <span class="truncate text-sm">{opt.label.split('')[0]}</span>
        <Badge size="xs" variant="soft" intent={isSelected ? 'success' : 'neutral'}>
          {opt.label.split('')[1]}
        </Badge>
      </div>
      {#if isSelected}
        <CheckIcon size={14} class="text-primary" />
      {/if}
    </div>
  {/snippet}
</Combobox>

03 Customization

Command Palette

A Spotlight-style command palette built with slotClasses on the base and input slots.
<Combobox
  options={[
    { label: '⌘K  Open Command Palette', value: 'cmd-k' },
    { label: '⌘P  Quick Open File', value: 'cmd-p' },
    { label: '⌘⇧P  Show All Commands', value: 'cmd-shift-p' },
    { label: '⌘B  Toggle Sidebar', value: 'cmd-b' },
    { label: '⌘J  Toggle Terminal', value: 'cmd-j' },
    { label: '⌘,  Open Settings', value: 'cmd-comma' }
  ]}
  aria-label="Command palette"
  placeholder="Type a command…"
  size="lg"
  slotClasses={{
    base: 'w-full',
    input:
      'rounded-xl shadow-[var(--blocks-shadow-lg)] ring-2 ring-primary/20 focus-visible:ring-primary/50 transition-all',
    listbox: 'rounded-xl shadow-[var(--blocks-shadow-lg)]'
  }}
/>

Glassmorphism

Frosted glass input for hero sections or overlay contexts.
<Combobox
  aria-label="Timezone"
  options={timezones}
  placeholder="Select your timezone…"
  unstyled
  slotClasses={{
    base: 'relative w-full',
    input:
      'w-full rounded-xl border border-white/20 bg-white/10 px-5 py-3 text-white placeholder-white/50 shadow-lg backdrop-blur-md transition-all focus-visible:border-white/40 focus-visible:bg-white/15 focus-visible:outline-none',
    listbox:
      'absolute z-[var(--z-dropdown)] mt-2 w-full rounded-xl border border-white/20 bg-white/10 p-1 shadow-xl backdrop-blur-xl max-h-60 overflow-y-auto',
    option:
      'flex w-full items-center gap-2 rounded-lg px-4 py-2.5 text-white/80 cursor-pointer transition-colors hover:bg-white/15',
    optionActive: 'bg-white/20 text-white',
    optionSelected: 'text-white font-medium',
    noResults: 'px-4 py-3 text-center text-white/50 text-sm',
    chevron:
      'absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/40 pointer-events-none'
  }}
/>

Terminal / Monospace

Fully unstyled rebuild with a terminal aesthetic.
<Combobox
  aria-label="Language"
  options={languages}
  placeholder="$ select --lang"
  unstyled
  slotClasses={{
    base: 'relative w-full font-mono',
    input:
      'w-full bg-neutral-950 text-green-400 border-2 border-green-600/50 rounded-none px-4 py-3 text-sm placeholder:text-green-600/50 focus-visible:outline-none focus-visible:border-green-400',
    listbox:
      'absolute z-[var(--z-dropdown)] mt-0 w-full bg-neutral-950 border-2 border-t-0 border-green-600/50 max-h-60 overflow-y-auto',
    option:
      'flex w-full items-center gap-2 px-4 py-2 text-sm text-green-300 cursor-pointer hover:bg-green-900/30',
    optionActive: 'bg-green-800/40 text-green-200',
    optionSelected: 'text-green-100 font-bold',
    noResults: 'px-4 py-3 text-center text-green-700 text-sm',
    chevron:
      'absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-green-600/50 pointer-events-none'
  }}
/>

A search-field skin used in more than one place — command palette, hero search — is better registered as a BlocksProvider preset (presets.Combobox) than repeated slotClasses. See Customization.

04 Accessibility

Built-in ARIA

The input uses role="combobox" with aria-expanded, aria-controls, and aria-autocomplete="list". The listbox uses role="listbox" and each option uses role="option" with aria-selected.

Keyboard

/ to navigate options. Enter to select. Escape to close. Home / End to jump to first / last option. Disabled options are skipped during navigation.

Active Descendant

Focus stays on the input at all times. The visually highlighted option is communicated via aria-activedescendant, keeping screen readers synchronized without moving DOM focus.

05 API Reference

40 props
40 props
Prop
Type
Default
Description

06 Types

Local type definitions used by this component.

13 types
Name
Kind
Category
Used by
Description

07 Installation

Import

import { Combobox } from '@urbicon-ui/blocks';