Components
Combobox
A filterable picker over a list of options — button or inline-input trigger, single or multi-select.
<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 →
use Livewire\Component;
class Demo extends Component
{
public string $framework = '';
public function render()
{
return view('livewire.demo');
}
}
<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. |