<x-bt-select />
Customize via presets →
Shared presets
x-bt-input component.
Basic Usage
Pass an associative array where keys are values and array values are labels.
1<x-bt-select2 label="Status"3 :options="[4 'draft' => 'Draft',5 'review' => 'In review',6 'published' => 'Published',7 ]"8 placeholder="Select status..."9/>
Options Formats
The select accepts multiple data shapes. Everything is normalized internally.
Simple Array
Values and labels are the same string.
1<x-bt-select label="Fruit" :options="['Apple', 'Banana', 'Orange']" />
Associative Array
Keys become the stored value, array values become the display label.
1<x-bt-select2 label="Status"3 :options="['active' => 'Active', 'inactive' => 'Inactive', 'archived' => 'Archived']"4/>
Array of Objects
Each item can have value, label, description, icon, and avatar keys.
1@php2$objectOptions = [3 ['value' => 1, 'label' => 'Alice', 'description' => 'Admin'],4 ['value' => 2, 'label' => 'Bob', 'description' => 'Editor'],5 ['value' => 3, 'label' => 'Carol', 'description' => 'Viewer'],6];7@endphp8<x-bt-select label="User" :options="$objectOptions" />
Eloquent Collection
Use option-label, option-value, and option-description to map model fields.
1<x-bt-select2 label="User"3 :options="$users"4 option-label="name"5 option-value="id"6 option-description="email"7 placeholder="Search a user..."8 searchable9/>
Rich Options (Icons & Avatars)
Options can include icon (Heroicon name, emoji, or raw SVG) and avatar (URL or emoji). Both render automatically in the dropdown.
1<x-bt-select2 label="Country Code"3 :options="$richOptions"4 placeholder="Select country..."5/>
Slot-Based Options
Declare options directly in Blade using <x-bt-option> child components instead of a PHP array.
1<x-bt-select name="country" label="Country">2 <x-bt-option value="AR" label="Argentina" />3 <x-bt-option value="US" label="United States" />4 <x-bt-option value="BR" label="Brazil" />5</x-bt-select>
Mixing Prop + Slot Options
Slot options merge after prop options. If a slot option has the same value as a prop option, the slot option wins (here DE becomes "Deutschland").
1<x-bt-select name="mix" label="Mixed Sources" :options="['FR' => 'France', 'DE' => 'Germany']">2 <x-bt-option value="ES" label="Spain" />3 <x-bt-option value="DE" label="Deutschland" />4</x-bt-select>
Multiple Selection
Enable :multiple="true" for multi-select. Selected values display as chips with a +N badge for overflow. Each chip has a remove button, and the dropdown shows checkboxes.
1<x-bt-select 2 label="Tags" 3 :options="[ 4 'frontend' => 'Frontend', 5 'backend' => 'Backend', 6 'devops' => 'DevOps', 7 'design' => 'Design', 8 ]" 9 :multiple="true"10 placeholder="Select one or more tags..."11/>
Searchable & Clearable
Both are enabled by default. Disable them explicitly when needed.
1<x-bt-select 2 label="Not Searchable" 3 :options="['A' => 'Option A', 'B' => 'Option B', 'C' => 'Option C']" 4 :searchable="false" 5/> 6 7<x-bt-select 8 label="Not Clearable" 9 :options="['A' => 'Option A', 'B' => 'Option B', 'C' => 'Option C']"10 :clearable="false"11/>
Sizes
1<x-bt-select xs label="Extra Small" :options="['A', 'B', 'C']" />2<x-bt-select sm label="Small" :options="['A', 'B', 'C']" />3<x-bt-select label="Medium (default)" :options="['A', 'B', 'C']" />4<x-bt-select lg label="Large" :options="['A', 'B', 'C']" />5<x-bt-select xl label="Extra Large" :options="['A', 'B', 'C']" />
Colors
The primary color (default) uses neutral gray with beartropy accents. Colors affect the dropdown background, option hover/active/selected states, chip styling, and border.
1<x-bt-select label="Default (primary)" :options="['A', 'B', 'C']" />2<x-bt-select blue label="Blue" :options="['A', 'B', 'C']" />3<x-bt-select red label="Red" :options="['A', 'B', 'C']" />4<x-bt-select green label="Green" :options="['A', 'B', 'C']" />5<x-bt-select purple label="Purple" :options="['A', 'B', 'C']" />
Dynamic colors
<x-bt-select :color="$dynamicColor" />
Remote Data
Fetch options from an API endpoint. The component handles search debouncing, pagination, and infinite scroll automatically.
Remote Search
Enable remote and provide a remote-url. The component sends ?q=search&page=1&per_page=20 on each search/scroll.
1<x-bt-select2 label="User (remote search)"3 placeholder="Type to search..."4 remote5 searchable6 remote-url="{{ route('api.users.select') }}"7/>
Infinite Scroll
Set a small per-page value. When the user scrolls near the bottom of the dropdown, the next page is fetched automatically. A "Loading..." indicator appears during fetch.
1<x-bt-select2 label="Infinite Scroll (5 per page)"3 placeholder="Scroll to load more..."4 remote5 searchable6 :per-page="5"7 remote-url="{{ route('api.users.select') }}"8/>
Deferred Fetch
By default, remote selects fetch on page load. Use :defer="true" to delay the first fetch until the user opens the dropdown. Ideal when you have many remote selects on one page.
1<x-bt-select2 label="Deferred Load"3 placeholder="Click to load users..."4 remote5 :defer="true"6 remote-url="{{ route('api.users.select') }}"7/>
Controller Example
Your endpoint receives q, page, and per_page query params. Return a JSON object with options (keyed by ID) and hasMore (boolean).
1use App\Models\User; 2use Illuminate\Http\Request; 3 4class UserSelectController extends Controller 5{ 6 public function index(Request $request) 7 { 8 $q = $request->input('q'); 9 $page = (int) $request->input('page', 1);10 $perPage = (int) $request->input('per_page', 20);11 12 $query = User::query();13 14 if ($q) {15 $query->where(function ($sub) use ($q) {16 $sub->whereRaw('LOWER(name) LIKE ?', ['%' . strtolower($q) . '%'])17 ->orWhereRaw('LOWER(email) LIKE ?', ['%' . strtolower($q) . '%']);18 });19 }20 21 $total = $query->count();22 23 $options = $query24 ->orderBy('name')25 ->offset(($page - 1) * $perPage)26 ->limit($perPage)27 ->get()28 ->mapWithKeys(fn ($user) => [29 $user->id => [30 'label' => $user->name,31 'avatar' => $user->avatar,32 'description' => $user->email,33 ],34 ]);35 36 return response()->json([37 'options' => $options,38 'hasMore' => ($page * $perPage) < $total,39 ]);40 }41}
Expected JSON Response
Each key in options is the option value. The object must have at least a label. Optional fields: description, icon, avatar.
1{2 "options": {3 "1": { "label": "Alice Smith", "avatar": "https://...", "description": "alice@example.com" },4 "2": { "label": "Bob Jones", "description": "bob@example.com" }5 },6 "hasMore": true7}
Slots
Start Slot
Add custom content at the start of the trigger.
1<x-bt-select label="With Prefix" :options="['A', 'B', 'C']">2 <x-slot:start>3 <x-bt-button color="gray" soft>Prefix</x-bt-button>4 </x-slot:start>5</x-bt-select>
Before & After Options
before-options renders above the options list. after-options renders below — and replaces the "No results" message when the filtered list is empty.
1<x-bt-select 2 label="With Dropdown Slots" 3 :options="[ 4 'draft' => 'Draft', 5 'review' => 'In review', 6 'published' => 'Published', 7 ]" 8> 9 <x-slot:before-options>10 <div class="p-2 text-sm text-gray-500">11 <em>Recently used</em>12 </div>13 </x-slot:before-options>14 <x-slot:after-options>15 <div class="p-2 text-sm text-gray-500 space-x-3">16 <em>Can't find what you need?</em>17 <a href="#" class="text-blue-600 hover:underline">Create new</a>18 </div>19 </x-slot:after-options>20</x-bt-select>
Select Inside Input
The Select can be placed inside an Input's start or end slot. Chrome (borders, shadows) is automatically stripped.
Phone Number Pattern
1<x-bt-input label="Phone" placeholder="123-456-7890">2 <x-slot:start>3 <x-bt-select :options="['+1 US', '+44 UK', '+34 ES']" placeholder="Code" />4 </x-slot:start>5</x-bt-input>
Wider Dropdown (fit-trigger)
When inside a narrow slot, the dropdown may be too narrow. Use :fit-trigger="false" so the dropdown uses min-width instead of matching the trigger exactly.
1<x-bt-input label="Phone (wider dropdown)" placeholder="123-456-7890">2 <x-slot:start>3 <x-bt-select4 :options="['+1 United States', '+44 United Kingdom', '+34 Spain', '+49 Germany']"5 placeholder="Code"6 :fit-trigger="false"7 />8 </x-slot:start>9</x-bt-input>
Livewire Integration
Deferred Binding
1<x-bt-select id="select-user-deferred" wire:model="userId" :options="$users" option-label="name" option-value="id" label="Deferred Binding" />
Live Binding
Use wire:model.live to update the Livewire property immediately on change.
Selected: none
1<x-bt-select id="select-live-category" wire:model.live="liveCategory" :options="['tech' => 'Technology', 'health' => 'Health', 'sports' => 'Sports']" label="Live Binding" />2<p class="text-sm text-gray-500 mt-2">Selected: <strong>{{ $liveCategory ?? 'none' }}</strong></p>
Multiple with wire:model
For multiple selects, the Livewire property should be an array.
1<x-bt-select id="select-tags" wire:model="selectedTags" :multiple="true" :options="['laravel' => 'Laravel', 'vue' => 'Vue', 'react' => 'React', 'alpine' => 'Alpine']" label="Multiple with wire:model" />
Autosave
Automatically calls a Livewire method on every change. The trigger border shows state feedback: gray (saving), green (ok), red (error). Requires wire:model.
1<x-bt-select 2 id="select-autosave" 3 label="Default status" 4 :options="[ 5 'draft' => 'Draft', 6 'review' => 'In review', 7 'published' => 'Published', 8 ]" 9 wire:model="defaultStatus"10 :autosave="true"11 autosave-method="savePreference"12 autosave-key="defaultStatus"13/>
Livewire Component
1public ?string $defaultStatus = null;2 3public function savePreference($value, $key)4{5 $this->toast()->success("Saved {$key} as {$value}");6}
Validation Errors
Errors are automatically detected from the Laravel validation error bag using the wire:model name. You can also force an error with custom-error.
Auto Validation
Submit without selecting a role to see the error. The border turns red and the error message appears below.
1<x-bt-select id="select-role" wire:model="validatedRole" :options="['admin' => 'Admin', 'editor' => 'Editor', 'viewer' => 'Viewer']" label="Role" />2<x-bt-button wire:click="submitRole" solid blue class="mt-3">Submit</x-bt-button>
Custom Error
Force an error state regardless of validation using custom-error.
Please select a valid status
1<x-bt-select2 label="Status"3 :options="['a' => 'Active', 'i' => 'Inactive']"4 :custom-error="'Please select a valid status'"5/>
Help Text
Choose your preferred category
Required field
1<x-bt-select label="Category" :options="['A', 'B', 'C']" help="Choose your preferred category" />2<x-bt-select label="Priority" :options="['Low', 'Medium', 'High']" hint="Required field" />
Keyboard Navigation
Full keyboard support with ARIA attributes for screen readers.
| Key | Dropdown Closed | Dropdown Open |
|---|---|---|
| Arrow Down | Open dropdown | Move highlight down |
| Arrow Up | Open dropdown | Move highlight up |
| Enter | Open dropdown | Select highlighted option |
| Space | Open dropdown | Type in search (if searchable) |
| Escape | — | Close dropdown |
| Tab | Normal tab | Normal tab |
Navigation wraps circularly. The highlighted option scrolls into view automatically. Mouse hover syncs with keyboard highlight.
ARIA attributes (role="listbox", role="option", aria-selected) are included for screen reader support.
Real-World Example
A task creation form combining selects with a button.
1<div class="max-w-md space-y-4"> 2 <x-bt-select 3 label="Assign to" 4 :options="$users" 5 option-label="name" 6 option-value="id" 7 placeholder="Select team member..." 8 searchable 9 icon="user-group"10 />11 <x-bt-select12 label="Priority"13 :options="['low' => 'Low', 'medium' => 'Medium', 'high' => 'High', 'critical' => 'Critical']"14 placeholder="Select priority..."15 />16 <x-bt-button solid blue class="w-full">Create Task</x-bt-button>17</div>
Slots
| Slot | Description |
|---|---|
start
|
Custom content inside the trigger at the start.
|
end
|
Custom content inside the trigger at the end.
|
before-options
|
Content rendered at the top of the dropdown, after the search input.
|
after-options
|
Content rendered at the bottom of the dropdown. Replaces the empty message when no results match.
|
PROPS
Core props for the select component.
| Prop | Type | Default | Description |
|---|---|---|---|
options
|
array|Collection|null
|
null
|
Data source: simple arrays, associative arrays, arrays of objects, or Eloquent collections.
|
selected
|
mixed
|
null
|
Initially selected value.
|
label
|
string|null
|
null
|
Label text above the select.
|
placeholder
|
string|null
|
Select...
|
Placeholder text when nothing is selected.
|
color
|
string|null
|
config default
|
Color preset name.
|
size
|
string|null
|
md
|
Size preset:
xs, sm, md, lg, xl.
|
searchable
|
bool
|
true
|
Show search input in the dropdown. For local options, filtering is client-side. For remote, the query is sent to the endpoint.
|
clearable
|
bool
|
true
|
Show clear button when a value is selected.
|
multiple
|
bool
|
false
|
Enable multiple selection with chips.
|
icon
|
string|null
|
null
|
Trigger icon name.
|
initial-value
|
mixed
|
null
|
Initial value for non-Livewire usage.
|
help
|
string|null
|
null
|
Help text below the field.
|
hint
|
string|null
|
null
|
Alias for
help.
|
custom-error
|
mixed
|
null
|
Custom error message (bypasses validation bag).
|
spinner
|
bool
|
true
|
Show loading spinner during Livewire actions.
|
empty-message
|
string|null
|
No options found
|
Text when the options list is empty.
|
per-page
|
int
|
15
|
Results per page for remote/pagination.
|
fit-trigger
|
bool
|
true
|
Match dropdown width to trigger. Set
false to allow the dropdown to be wider.
|
REMOTE PROPS
Props for remote data fetching.
| Prop | Type | Default | Description |
|---|---|---|---|
remote
|
bool
|
false
|
Enable remote data fetching via
remote-url.
|
remote-url
|
string|null
|
null
|
API endpoint URL. Receives
?q=&page=&per_page=.
|
defer
|
bool
|
false
|
Defer remote fetch until dropdown opens for the first time.
|
OPTION MAPPING
Control which fields are extracted from each option object.
| Prop | Type | Default | Description |
|---|---|---|---|
option-label
|
string
|
label
|
Key for option label text. Fallbacks:
name, text, value.
|
option-value
|
string
|
value
|
Key for option value. Fallbacks:
id, key.
|
option-description
|
string
|
description
|
Key for option description. Fallbacks:
desc, subtitle.
|
option-icon
|
string
|
icon
|
Key for option icon (Heroicon name, emoji, SVG, or
<img>).
|
option-avatar
|
string
|
avatar
|
Key for option avatar (URL rendered as
<img>, or text/emoji).
|
AUTOSAVE PROPS
Auto-save selection on change via Livewire. Requires wire:model.
| Prop | Type | Default | Description |
|---|---|---|---|
autosave
|
bool
|
false
|
Enable autosave mode.
|
autosave-method
|
string
|
savePreference
|
Livewire method to call. Signature:
method($value, $key).
|
autosave-key
|
string|null
|
null
|
Key passed to the autosave method. Defaults to the
wire:model name.
|
autosave-debounce
|
int
|
300
|
Debounce delay in milliseconds.
|