Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Searchable Dropdown Laravel Package

teofanis/searchable-dropdown

A Laravel package that adds a searchable dropdown/select component to Blade forms. It offers filtering, customizable options, and a simple API for replacing long select lists with a fast, user-friendly searchable input.

View on GitHub
Deep Wiki
Context7

Getting Started

Check your Livewire version first. 2.0.1 exists because of Livewire v3 support (PR #13). The jump from 1.5.2 → 2.0.1 is a major bump: the 2.x line assumes a Livewire v3 runtime. If you're still on Livewire v2, pin teofanis/searchable-dropdown:^1.5 and skip this release entirely.

composer require teofanis/searchable-dropdown

Confirm the constraints resolved cleanly — composer show teofanis/searchable-dropdown should show a livewire/livewire constraint matching your installed version. A silent downgrade to 1.5.x here is the usual cause of "the component just doesn't work".

Where to look first:

  1. The package README for the v2 component tag and its parameter list (it changed alongside the Livewire bump).
  2. vendor/teofanis/searchable-dropdown/resources/views/ — read the Blade view before you use it, so you know what markup and classes you're inheriting.
  3. The service provider: note whether config/views are publishable and whether assets need registering.

First use case: replacing a <select> that renders thousands of <option> tags. A user picker on an edit form is the canonical fit — type three characters, hit the server, get back a trimmed result set, bind the chosen id to your model.

<livewire:searchable-dropdown
    wire:model="userId"
    :options="$this->userSearchResults"
/>

Get that one case working end-to-end (select → save → rehydrate on reload) before adding multi-select or custom option slots.

Implementation Patterns

Treat the dropdown as a form input, not a widget. Bind with wire:model to a public property on the parent Livewire component, then validate normally in your save() method. Keep the selected id in the property and resolve the display label separately — don't try to round-trip an Eloquent model through the component.

Server-side search with a callback. The package's value is that it doesn't ship your whole table to the browser. Scope the result set inside the component rather than passing in an unbounded collection:

public function searchUsers(string $term)
{
    return User::query()
        ->when($term, fn ($q) => $q->where('name', 'like', "{$term}%"))
        ->orderBy('name')
        ->limit(20)
        ->get(['id', 'name']);
}

Always cap the query. An uncapped ->get() inside a search handler is the single fastest way to take down a page.

Live v3 model binding. In v3, wire:model is deferred until the next request, so the selected value won't be available in your updated() / updatedFoo() hooks in the same request. Use wire:model.live (or .live.debounce.300ms if the component re-searches on every keystroke) when you need immediate reactivity — for example, a second dropdown whose options depend on the first selection.

Cascading selects. Parent → child is the most common integration: watch the parent property, reset the child, and rebuild the child's option source. Explicitly null the child value when the parent changes, otherwise you'll persist a pairing that no longer exists.

Custom option rendering. If your model has a status badge, avatar, or secondary line (email, department), use the package's option slot rather than post-processing labels in PHP. Publish the views and override locally so the customisation survives upgrades.

Testing. Assert the component's bound property, not the rendered markup — the option list is often server-rendered on demand:

Livewire::test(EditUser::class)
    ->set('userId', $user->id)
    ->call('save')
    ->assertHasNoErrors();

Add one test that mounts the dropdown component itself with a search term and asserts the result count, so a future Livewire bump fails in CI rather than in a user's browser.

Gotchas and Tips

This is a breaking release for a reason. The 2.0 line targets Livewire v3, which changed the component lifecycle, the event bus (emit() → dispatch()), and default binding behaviour. If any of your app code hooks into events this package emits, or extends its component class, re-read those customisations after upgrading — they will not have been updated for you.

Check for v2-only assumptions in the parent component. Common breakages after the upgrade:

  • $this->emit(...) listeners registered on the parent — v3 renamed these to dispatch/#[On].
  • Alpine plugins loaded via @livewireScripts config that no longer applies in v3.
  • wire:key collisions in option loops. In v3's DOM diffing, a missing unique wire:key per option produces duplicate selections and stale labels that look like a package bug but aren't.

Debounce the search. The default request-per-keystroke behaviour is expensive on a slow query. Add .debounce.300ms on the search binding, and confirm your DB has an index on the column you're LIKE-ing — a leading-wildcard LIKE '%term%' will not use it.

Empty and null values. Decide explicitly whether "no selection" is null or '', and make your validation rule match (nullable vs required). A dropdown that submits '' against a nullable|exists:users,id rule passes validation unexpectedly.

Styling bleed. The published Blade view brings its own Tailwind classes. If your app's Tailwind config purges vendor paths, those classes get stripped and the dropdown renders as an unstyled <ul>. Add the vendor view path to content in tailwind.config.js, or publish and restyle the view.

Debugging. In Livewire v3, open the browser network tab and watch the livewire/update requests: the payload shows exactly which property the dropdown set and what the server returned. That single view resolves most "it selects but doesn't save" reports faster than dump-and-die.

Extension points to prefer, in order: publish the config → publish the views → pass options/props → only then subclass. Subclassing the component couples you to internals that clearly do change between majors.

Pin the major. Given the 1.5 → 2.0 break, constrain to ^2.0 in composer.json rather than *, and put the Livewire major you actually run in your upgrade notes — this package's version is now effectively a function of your Livewire version.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle