Logo Beartropy

Selects

The x-select component is a powerful, customizable dropdown:
Supports icons/avatars per option, multi-select, clearable fields, async/remote search, lazy loading/infinite scroll, validation and more.
All features can be combined seamlessly for advanced UX.
You can customize this component


Basic Usage

Basic Usage

  • Pass a plain array, an array of objects, or an Eloquent-like collection to :options.
  • Each option can define: label, icon, avatar, description, etc.
  • Single selection by default.
+ Seleccionar...
+ Seleccionar...
+ Choose a category...
+ Seleccionar...
blade
            
1<x-select :options="['Option 1', 'Option 2', 'Option 3']" label="Simple array" clearable />
2 
3<x-select
4 :options="[
5 ['id' => 1, 'label' => 'Option A', 'icon' => '⭐'],
6 ['id' => 2, 'label' => 'Option B', 'icon' => '🌵'],
7 ['id' => 3, 'label' => 'Option C', 'icon' => '🎯'],
8 ]"
9 label="Object array"
10 clearable
11/>
12 
13<x-select :options="$categories" label="Categories" placeholder="Choose a category..." clearable />
14 
15<x-select :options="[
16 ['id' => 1, 'label' => 'Frontend', 'avatar' => 'https://randomuser.me/api/portraits/men/5.jpg'],
17 ['id' => 2, 'label' => 'Backend', 'avatar' => 'https://randomuser.me/api/portraits/women/22.jpg'],
18 ['id' => 3, 'label' => 'Mobile', 'avatar' => null],
19]" label="With avatars" clearable />

Multi-Select, Search, Clearable

  • multiple: allows multi-selection (returns array).
  • clearable: adds an X to remove selection.
  • searchable: enables input to filter options (works local/remote).
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
blade
            
1<x-select :options="$categories" label="Categories" multiple clearable />
2<x-select :options="$categories" label="Categories (search)" searchable />
3<x-select :options="$categories" label="Multi search" multiple searchable clearable />

Option Features: Icons, Avatars, Descriptions

  • Each option can have an icon (SVG, emoji, string), avatar (image), and/or description (below label).
+ Seleccionar...
blade
            
1<x-select :options="[
2 ['id'=>1, 'label'=>'Design', 'avatar'=>'https://randomuser.me/api/portraits/men/5.jpg', 'description'=>'UI/UX'],
3 ['id'=>2, 'label'=>'Development', 'avatar'=>'https://randomuser.me/api/portraits/women/22.jpg', 'description'=>'Backend/Frontend'],
4 ['id'=>3, 'label'=>'Marketing', 'icon'=>'💼', 'description'=>'SEO/Content'],
5 ['id'=>4, 'label'=>'🤖 Bot', 'icon'=>'🤖'],
6]" label="Icons/avatars/desc" clearable />

Remote/Async & Lazy Loading

  • remote + remote-url: fetch options from any API endpoint.
  • lazy: infinite scroll, loads more as you scroll (local or remote).
  • For remote endpoints, response should be { id: { label, ... } } or { options: {...}, hasMore: bool }.
+ Search country...
blade
            
1<x-select
2 remote
3 remote-url="{{ route('api.countries.select.lazy') }}"
4 label="Lazy country"
5 placeholder="Search country..."
6 lazy
7 searchable
8/>

Presets & Colors

+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
blade
            
1<x-select :options="$categories" label="Default" />
2<x-select :options="$categories" blue label="Blue" />
3<x-select :options="$categories" emerald label="Emerald" />
4<x-select :options="$categories" indigo label="Indigo" />
5<x-select :options="$categories" rose label="Rose" />
6<x-select :options="$categories" amber label="Amber" />
7<x-select :options="$categories" cyan label="Cyan" />
8<x-select :options="$categories" violet label="Violet" />

Filled

+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
+ Seleccionar...
blade
            
1<x-select fill :options="$categories" label="Default" />
2<x-select fill :options="$categories" blue label="Blue" />
3<x-select fill :options="$categories" emerald label="Emerald" />
4<x-select fill :options="$categories" indigo label="Indigo" />
5<x-select fill :options="$categories" rose label="Rose" />
6<x-select fill :options="$categories" amber label="Amber" />
7<x-select fill :options="$categories" cyan label="Cyan" />
8<x-select fill :options="$categories" violet label="Violet" />

Validation & Error States

Validation with Livewire

  • Works perfectly with wire:model for Livewire validation.
  • Shows red border and error message automatically if validation fails.
  • Multi-select returns array, single-select returns value.
+ Seleccionar...

You must select a category

blade
            
1// In your Livewire component:
2public $selectedCategory = null;
3protected $rules = ['selectedCategory' => 'required'];
4 
5<x-select
6 :options="$categories"
7 label="Category"
8 wire:model="selectedCategory"
9 clearable
10/>

Async Controller Examples

Async Controller with Lazy Loading

Example for async controller with lazy loading support (infinite scroll).
Tip: For large lists, always implement hasMore.
php
            
1// routes/web.php
2Route::get('/api/country/select', [CountrySelectLazyController::class, 'index'])->name('api.countries.select.lazy');
3 
4// app/Http/Controllers/CountrySelectLazyController.php
5<?php
6 
7namespace App\Http\Controllers;
8 
9use App\Models\Country;
10use Illuminate\Http\Request;
11 
12class CountrySelectLazyController extends Controller
13{
14 public function index(Request $request)
15 {
16 $q = $request->input('q');
17 $page = (int) $request->input('page', 1);
18 $perPage = (int) $request->input('per_page', 20);
19 
20 $query = Country::query();
21 
22 if ($q) {
23 $query->where(function ($sub) use ($q) {
24 $sub->whereRaw('LOWER(name) LIKE ?', ['%' . strtolower($q) . '%']);
25 });
26 }
27 
28 $total = $query->count();
29 
30 $countries = $query
31 ->orderBy('country')
32 ->offset(($page - 1) * $perPage)
33 ->limit($perPage)
34 ->get()
35 ->mapWithKeys(function ($country) {
36 return [
37 $country->id => [
38 'label' => $country->name,
39 ]
40 ];
41 });
42 
43 $hasMore = ($page * $perPage) < $total;
44 
45 return response()->json([
46 'options' => $countries,
47 'hasMore' => $hasMore,
48 ]);
49 }
50 
51}
Code highlighting provided by Torchlight