Skip to content

Combobox

Presents a selection of choices to the user, filtered by a text input.

Props


Features

  • 🎹 Keyboard navigation
  • 🍃 Multi-selection mode
  • 🧠 Smart focus management
  • 💬 Flexible filtering

Usage

<script lang="ts">
import { Combobox } from "melt/builders";
const options = [ /* ... */ ] as const;
type Option = (typeof options)[number];
const combobox = new Combobox<Option>();
const filtered = $derived.by(() => {
if (!combobox.touched) return options;
return options.filter((o) =>
o.toLowerCase().includes(combobox.inputValue.trim().toLowerCase()),
);
});
</script>
<label for={combobox.ids.input}>Favorite Character</label>
<input {...combobox.input} />
<button {...combobox.trigger}>open</button>
<div {...combobox.content}>
{#each filtered as option (option)}
<div {...combobox.getOption(option)}>
{option}
{#if combobox.isSelected(option)}
{/if}
</div>
{:else}
<span>No results found</span>
{/each}
</div>

Customizing floating elements

Floating elements use Floating UI under the hood. To this end, we expose a floatingConfig option, which can be used to control the underlying computePosition function, its middlewares, and the resulting styling that is applied.

API Reference

Constructor Props

The props that are passed when calling
new Combobox()
    export type ComboboxProps<T extends string, Multiple extends boolean = false> = Omit<
    PopoverProps,
    "closeOnEscape" | "closeOnOutsideClick" | "sameWidth"
    > & {
    /**
    * If `true`, multiple options can be selected at the same time.
    *
    * @default false
    */
    multiple?: MaybeGetter<Multiple | undefined>;
    /**
    * The value for the Combobox.
    *
    * When passing a getter, it will be used as source of truth,
    * meaning that the value only changes when the getter returns a new value.
    *
    * Otherwise, if passing a static value, it'll serve as the default value.
    *
    *
    * @default false
    */
    value?: MaybeMultiple<T, Multiple>;
    /**
    * Called when the value is supposed to change.
    */
    onValueChange?: OnMultipleChange<T, Multiple>;
    /**
    * The currently highlighted value.
    */
    highlighted?: MaybeGetter<T | null | undefined>;
    /**
    * Called when the highlighted value changes.
    */
    onHighlightChange?: (highlighted: T | null) => void;
    /**
    * Determines behavior when scrolling items into view.
    * Set to null to disable auto-scrolling.
    *
    * @default "nearest"
    * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#block
    */
    scrollAlignment?: MaybeGetter<"nearest" | "center" | null | undefined>;
    /**
    * If the content should have the same width as the trigger
    *
    * @default true
    */
    sameWidth?: MaybeGetter<boolean | undefined>;
    };

Methods

The methods returned from
new Combobox()
  • select

    (value: T) => void
  • scrollIntoView

    (value?: T | undefined) => void
  • getOptionId

    (value: T) => string
  • getOption

    (
    value: T,
    onSelect?: (() => void) | undefined,
    ) => {
    readonly id: string
    readonly "data-melt-combobox-option": ""
    readonly "data-value": DataReturn<T>
    readonly "aria-hidden": true | undefined
    readonly "aria-selected": boolean
    readonly "data-highlighted": "" | undefined
    readonly role: "option"
    readonly onmouseover: () => void
    readonly onclick: () => void
    }
    Gets the attributes for the option element. @param value The value of the option. @param onSelect An optional callback to call when the option is selected, overriding the default behavior. @returns The attributes for the option element.
  • getOptionsEls

    () => HTMLElement[]
  • getOptions

    () => T[]
  • highlight

    (value: T) => void
  • highlightNext

    () => void
  • highlightPrev

    () => void
  • highlightFirst

    () => void
  • highlightLast

    () => void

Properties

The properties returned from
new Combobox()
  • multiple

    Multiple
  • scrollAlignment

    "nearest" | "center" | null
  • inputValue

    string
  • touched

    boolean
  • onSelectMap

    Map<T, () => void>
  • ids

    {
    trigger: string
    content: string
    option: string
    input: string
    } & { invoker: string; popover: string }
  • isSelected

    (value: T) => boolean
  • value

    SelectionStateValue<T, Multiple>
  • highlighted

    T | null
  • valueAsString

    string
  • input

    {
    readonly "data-melt-combobox-input": ""
    readonly id: string
    readonly role: "combobox"
    readonly "aria-expanded": boolean
    readonly "aria-controls": string
    readonly "aria-owns": string
    readonly onclick: undefined
    readonly value: string
    readonly oninput: (e: Event) => void
    readonly onkeydown: (e: KeyboardEvent) => void
    readonly onfocusout: () => Promise<void>
    readonly popovertarget: string
    }
  • trigger

    {
    onfocusout: () => Promise<void>
    "data-melt-combobox-trigger": string
    id: string
    onclick: () => void
    }
  • content

    {
    readonly onfocusout: () => Promise<void>
    readonly id: string
    readonly popover: "manual"
    readonly ontoggle: (
    e: ToggleEvent & { currentTarget: EventTarget & HTMLElement },
    ) => void
    readonly tabindex: -1
    readonly inert: boolean
    readonly "data-open": "" | undefined
    } & {
    readonly "data-melt-combobox-content": ""
    readonly role: "listbox"
    readonly "aria-expanded": boolean
    readonly "aria-activedescendant": string | undefined
    }