Components

Combobox

A filterable picker over a list of options — button or inline-input trigger, single or multi-select.

php artisan blatui:add combobox
<x-ui.combobox
    placeholder="Select framework..."
    searchPlaceholder="Search framework..."
    empty="No framework found."
    :options="[
        ['value' => 'next', 'label' => 'Next.js'],
        ['value' => 'sveltekit', 'label' => 'SvelteKit'],
        ['value' => 'nuxt', 'label' => 'Nuxt.js'],
        ['value' => 'remix', 'label' => 'Remix'],
        ['value' => 'astro', 'label' => 'Astro'],
    ]"
/>

Disabled

<x-ui.combobox
    disabled
    placeholder="Select framework..."
    searchPlaceholder="Search framework..."
    empty="No framework found."
    :options="[
        ['value' => 'next', 'label' => 'Next.js'],
        ['value' => 'sveltekit', 'label' => 'SvelteKit'],
        ['value' => 'nuxt', 'label' => 'Nuxt.js'],
        ['value' => 'remix', 'label' => 'Remix'],
        ['value' => 'astro', 'label' => 'Astro'],
    ]"
/>

Indicators

{{-- Choose how a selected option is marked: `check` (default), `checkbox`
     (pairs well with multiple) or `radio` (pairs well with single-select). --}}
<div class="flex flex-wrap items-start gap-4">
    <x-ui.combobox
        indicator="checkbox"
        multiple
        width="w-[220px]"
        placeholder="Select frameworks..."
        :value="['next']"
        :options="[
            ['value' => 'next', 'label' => 'Next.js'],
            ['value' => 'nuxt', 'label' => 'Nuxt.js'],
            ['value' => 'remix', 'label' => 'Remix'],
            ['value' => 'astro', 'label' => 'Astro'],
        ]"
    />

    <x-ui.combobox
        indicator="radio"
        width="w-[220px]"
        placeholder="Select a framework..."
        :value="'next'"
        :options="[
            ['value' => 'next', 'label' => 'Next.js'],
            ['value' => 'nuxt', 'label' => 'Nuxt.js'],
            ['value' => 'remix', 'label' => 'Remix'],
            ['value' => 'astro', 'label' => 'Astro'],
        ]"
    />
</div>

Inline Async

{{--
    Async options: the inline-input combobox is just a listbox over its reactive `options`,
    so you can swap that array in from a server as the user types. This demo debounces the
    query, fakes a fetch against a static dataset, then assigns the result onto the
    component's `options` via Alpine.$data(). Swap `fetchOptions` for a real
    `fetch('/api/...').then(r => r.json())`.
--}}
<div
    x-data="{
        loading: false,
        _t: null,
        all: [
            { value: 'next', label: 'Next.js' },
            { value: 'svelte', label: 'SvelteKit' },
            { value: 'nuxt', label: 'Nuxt' },
            { value: 'remix', label: 'Remix' },
            { value: 'astro', label: 'Astro' },
            { value: 'laravel', label: 'Laravel' },
            { value: 'rails', label: 'Ruby on Rails' },
            { value: 'django', label: 'Django' },
        ],
        fetchOptions(q) {
            // Pretend this is a network round-trip.
            const needle = q.toLowerCase();
            return new Promise((resolve) =>
                setTimeout(() => resolve(this.all.filter((o) => o.label.toLowerCase().includes(needle))), 350),
            );
        },
        // The native `input` event bubbles from the field; read the combobox's reactive
        // state with Alpine.$data and replace its `options` after the fetch settles.
        onSearch(e) {
            const cmp = Alpine.$data(e.target.closest('[data-slot=combobox]'));
            clearTimeout(this._t);
            this.loading = true;
            this._t = setTimeout(async () => {
                cmp.options = await this.fetchOptions(cmp.query);
                this.loading = false;
            }, 250);
        },
    }"
    class="w-[280px]"
    @input="onSearch($event)"
    ::class="loading && 'opacity-70'"
>
    <x-ui.combobox trigger="input" width="w-full" placeholder="Search frameworks (async)..." />
</div>

Inline Icon

{{-- The inline-input trigger accepts a leading `icon` (any lucide name) and a `size`. --}}
@php($frameworks = [
    ['value' => 'next', 'label' => 'Next.js'],
    ['value' => 'svelte', 'label' => 'SvelteKit'],
    ['value' => 'nuxt', 'label' => 'Nuxt'],
    ['value' => 'remix', 'label' => 'Remix'],
    ['value' => 'astro', 'label' => 'Astro'],
    ['value' => 'laravel', 'label' => 'Laravel'],
])

<x-ui.combobox trigger="input" icon="search" :options="$frameworks" placeholder="Search framework..." />

Inline Input

{{-- `trigger="input"` makes the field itself the search box — the inline autocomplete shape. --}}
@php($frameworks = [
    ['value' => 'next', 'label' => 'Next.js'],
    ['value' => 'svelte', 'label' => 'SvelteKit'],
    ['value' => 'nuxt', 'label' => 'Nuxt'],
    ['value' => 'remix', 'label' => 'Remix'],
    ['value' => 'astro', 'label' => 'Astro'],
    ['value' => 'laravel', 'label' => 'Laravel'],
])

<x-ui.combobox trigger="input" :options="$frameworks" placeholder="Search framework..." />

Inline Multiple

{{-- Inline-input + multiple = a tag input: picks become removable chips inside the field
     and the list stays open after each pick. --}}
<x-ui.combobox
    trigger="input"
    multiple
    width="w-[280px]"
    placeholder="Search frameworks..."
    :value="['next']"
    :options="[
        ['value' => 'next', 'label' => 'Next.js'],
        ['value' => 'svelte', 'label' => 'SvelteKit'],
        ['value' => 'nuxt', 'label' => 'Nuxt'],
        ['value' => 'remix', 'label' => 'Remix'],
        ['value' => 'astro', 'label' => 'Astro'],
    ]"
/>

Inline Sizes

{{-- The inline-input trigger honours `size` (sm | default | lg). --}}
@php($frameworks = [
    ['value' => 'next', 'label' => 'Next.js'],
    ['value' => 'svelte', 'label' => 'SvelteKit'],
    ['value' => 'nuxt', 'label' => 'Nuxt'],
    ['value' => 'remix', 'label' => 'Remix'],
    ['value' => 'astro', 'label' => 'Astro'],
])

<div class="flex flex-col gap-3">
    <x-ui.combobox trigger="input" :options="$frameworks" size="sm" placeholder="Small" />
    <x-ui.combobox trigger="input" :options="$frameworks" size="default" placeholder="Default" />
    <x-ui.combobox trigger="input" :options="$frameworks" size="lg" placeholder="Large" />
</div>

Inline With Label

{{-- An inline-input combobox wired to a label + a form field name (submits as `framework`). --}}
@php($frameworks = [
    ['value' => 'next', 'label' => 'Next.js'],
    ['value' => 'svelte', 'label' => 'SvelteKit'],
    ['value' => 'nuxt', 'label' => 'Nuxt'],
    ['value' => 'remix', 'label' => 'Remix'],
    ['value' => 'astro', 'label' => 'Astro'],
])

<div class="grid w-[260px] gap-2">
    <x-ui.label for="framework">Framework</x-ui.label>
    <x-ui.combobox trigger="input" name="framework" :options="$frameworks" placeholder="Search framework..." />
</div>

Multiple

<x-ui.combobox
    multiple
    width="w-[260px]"
    placeholder="Select frameworks..."
    searchPlaceholder="Search framework..."
    empty="No framework found."
    :value="['next', 'remix']"
    :options="[
        ['value' => 'next', 'label' => 'Next.js'],
        ['value' => 'sveltekit', 'label' => 'SvelteKit'],
        ['value' => 'nuxt', 'label' => 'Nuxt.js'],
        ['value' => 'remix', 'label' => 'Remix'],
        ['value' => 'astro', 'label' => 'Astro'],
    ]"
/>

Non Searchable

{{-- A plain picker: `:searchable="false"` drops the search box. The panel matches the trigger width. --}}
<x-ui.combobox
    :searchable="false"
    placeholder="Select a plan…"
    width="w-[220px]"
    :options="[
        ['value' => 'free', 'label' => 'Free'],
        ['value' => 'pro', 'label' => 'Pro'],
        ['value' => 'team', 'label' => 'Team'],
        ['value' => 'enterprise', 'label' => 'Enterprise'],
    ]"
/>

Preselected

<x-ui.combobox
    value="remix"
    placeholder="Select framework..."
    searchPlaceholder="Search framework..."
    empty="No framework found."
    :options="[
        ['value' => 'next', 'label' => 'Next.js'],
        ['value' => 'sveltekit', 'label' => 'SvelteKit'],
        ['value' => 'nuxt', 'label' => 'Nuxt.js'],
        ['value' => 'remix', 'label' => 'Remix'],
        ['value' => 'astro', 'label' => 'Astro'],
    ]"
/>

Statuses

<x-ui.combobox
    placeholder="+ Set status"
    searchPlaceholder="Change status..."
    empty="No status found."
    width="w-[220px]"
    :options="[
        ['value' => 'backlog', 'label' => 'Backlog'],
        ['value' => 'todo', 'label' => 'Todo'],
        ['value' => 'in-progress', 'label' => 'In Progress'],
        ['value' => 'done', 'label' => 'Done'],
        ['value' => 'canceled', 'label' => 'Canceled'],
    ]"
/>

Using with Livewire

wire:model

The examples above are the frontend (Blade + Alpine) usage. Inside a Livewire component, bind wire:model for two-way state — same component, no wrappers. Full Livewire guide →

app/Livewire/Demo.php
use Livewire\Component;

class Demo extends Component
{
    public string $framework = '';

    public function render()
    {
        return view('livewire.demo');
    }
}
resources/views/livewire/demo.blade.php
<x-ui.combobox wire:model="framework" :options="['laravel' => 'Laravel', 'rails' => 'Rails']" />

API Reference

Props, slots and exposed methods for <x-ui.combobox>.

Props

Prop Type Default Description
options* array [] The selectable options. Each entry may be a plain string (used as both value and label) or an associative array with value and label. See Each option below.
value string|array '' The initially selected value. Pass an array of values when multiple is true.
name string Form field name. A hidden input mirrors the selection so it submits with a form (as name[] when multiple).
placeholder string Trigger text shown when nothing is selected. Defaults to a translatable "Select option..." string.
searchPlaceholder string Placeholder for the search input. Defaults to a translatable "Search..." string.
empty string Message shown when the search matches no options. Defaults to a translatable "No results found." string.
trigger string 'button' How the listbox is opened. "button" is the classic popover with a search input inside. "input" makes the field itself the search box — the inline autocomplete shape (replaces the deprecated <x-ui.autocomplete>).
button input
searchable bool true Show the search input (button trigger only). Set false for a plain picker that lists every option with no filtering.
size string 'default' Height and text size of the field (input trigger, single-select).
sm default lg
icon string Optional leading Lucide icon name, e.g. "search" (input trigger only).
multiple bool false Allow picking many values. Selected items render as removable chips and the list stays open after each pick.
indicator string 'check' How a selected option is marked in the list: a trailing check, a checkbox box (pairs well with multiple), or a radio dot (pairs well with single-select).
check checkbox radio
disabled bool false Disables the trigger so the list cannot be opened.
width string 'w-[200px]' Tailwind width class for the trigger; the popover matches the trigger's width.

* Required.

Each option

Prop Type Default Description
value* string The submitted value for this option.
label string Visible, searchable text for the option. Falls back to value when omitted.

Methods

Available on the component's Alpine scope — call them from markup in the slot (e.g. @click="…").

Method Description
openList() Opens the popover, clears the query and focuses the search input.
close() Closes the popover and returns focus to the trigger.
select(value) Selects the given value (toggles it when multiple). Single select also closes the list.
remove(value) Removes a value from the multiple selection.