williamug/searchable-select
Feature-rich searchable select for Laravel Livewire 3/4, powered by Alpine.js and styled with Tailwind. Supports single and multi-select, grouped options, cascading dropdowns, clear button, dark mode, accessibility, and real-time client-side search.
composer require williamug/searchable-select and ensure Tailwind CSS is configured to scan the package views (see Tailwind CSS Setup).wire:model with a simple Eloquent collection or array of options.
<x-searchable-select
wire:model="country_id"
:options="$countries"
placeholder="Select a country"
/>
tailwind.config.js or CSS file includes the package’s views (critical for styling).Data Preparation:
mount() method (e.g., Country::all()).:options.public function mount() {
$this->countries = Country::orderBy('name')->get();
}
Binding:
wire:model to bind to a Livewire property (no need for :selected-value).$wire.entangle().Validation:
save()).public function save() {
$this->validate(['country_id' => 'required|exists:countries,id']);
}
Rendering:
<label for="country">Country</label>
<x-searchable-select
wire:model="country_id"
:options="$countries"
placeholder="Select..."
/>
@error('country_id') <span>{{ $message }}</span> @enderror
wire:model (e.g., public $selected_skills = [];).:multiple="true".@if(!empty($selected_skills))
<div class="mt-2">
Selected: {{ count($selected_skills) }} skills
</div>
@endif
updatedPropertyName() in Livewire to reload child options when a parent changes.public function updatedCountryId($value) {
$this->regions = Region::where('country_id', $value)->get();
$this->region_id = null; // Reset child selection
}
<x-searchable-select
wire:model="region_id"
:options="$regions"
:disabled="!$country_id"
/>
$groupedOptions = [
['label' => 'North America', 'options' => [...]],
['label' => 'Europe', 'options' => [...]],
];
:grouped="true" and customize group labels:
<x-searchable-select
:options="$groupedOptions"
:grouped="true"
group-label="label"
group-options="options"
/>
optionValue/optionLabel for non-standard data structures:
<x-searchable-select
:options="$countries"
option-value="code"
option-label="country_name"
/>
Tailwind Purging:
tailwind.config.js includes the package’s views:
content: [
'./resources/**/*.blade.php',
'./vendor/williamug/searchable-select/resources/views/**/*.blade.php',
],
Livewire Property Mismatch:
wire:model matches the Livewire property name exactly (case-sensitive).Empty Options Handling:
<x-searchable-select
:options="$countries"
placeholder="No countries found"
/>
Multi-Select Array Initialization:
mount():
public $selected_skills = [];
Console Logs:
$wire events to debug interactions:
x-data="{
init() {
this.$wire.on('model.changed', (value) => {
console.log('Selected value:', value);
});
}
}"
Livewire Wire:model:
$wire object in the browser console:
console.log(this.$wire.get('country_id'));
Performance:
->take(100) or pagination to limit initial load.Dark Mode:
<x-searchable-select dark />
Custom Styling:
php artisan vendor:publish --tag=searchable-select-views
resources/views/components/searchable-select.blade.php.Accessibility:
<x-searchable-select
aria-label="Select a country"
/>
Clearable Behavior:
:clearable="false" if needed:
<x-searchable-select
:clearable="false"
/>
Validation Integration:
public function rules() {
return ['country_id' => 'required|exists:countries,id'];
}
@error('country_id')
<span class="text-red-500">{{ $message }}</span>
@enderror
Testing:
assertSet() and assertInput():
public function test_dropdown_selection() {
$this->set('country_id', 1);
$this->assertSet('country_id', 1);
}
How can I help you explore Laravel packages today?