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

Filament Picker Laravel Package

icetalker/filament-picker

Filament Picker adds a powerful picker form field to Filament, letting users search, browse, and select records from a table or modal instead of a plain dropdown. Great for large datasets and relation fields, with a smooth, native Filament look and feel.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require icetalker/filament-picker

The service provider is auto-discovered on Laravel 10/11, so no manual registration is normally required. If the README lists a php artisan vendor:publish step for config or assets, run it before rendering the field for the first time:

php artisan vendor:publish --tag="filament-picker-config"
php artisan vendor:publish --tag="filament-picker-assets"
# or the generic alias if the package ships one

Where to look first

  1. The package README — it is the only reliable source of the exact fluent method names (options(), multiple(), searchable(), etc.); the API surface is small but has changed between minor releases.
  2. vendor/icetalker/filament-picker/src/ — the field class (usually Picker.php) and its base class show every fluent method in one file.
  3. resources/views/ and resources/dist/ — the Blade view and the compiled JS/CSS tell you what is customizable without forking.
  4. config/filament-picker.php, if present, for defaults like grid columns or asset loading.

First use case

The fastest win is replacing a Select that has visually-distinct options (colors, icons, statuses, plan tiers, avatars) with a tile/grid picker so users can see the choices rather than read them:

use Icetalker\FilamentPicker\Forms\Components\Picker;

Picker::make('status')
    ->options([
        'draft'     => 'Draft',
        'review'    => 'In review',
        'published' => 'Published',
    ])
    ->required(),

Save it, load the form, and you should see the options rendered as pickable tiles instead of a dropdown. Confirm the stored value matches the option key — that is the single most common first-run surprise.

Implementation Patterns

Single-select from a static array

The bread-and-butter case. Keys become the stored value; labels become the visible tile. Keep keys as strings — numeric keys get coerced to ints by Livewire's hydration in ways that surprise assertFormSet tests.

Picker::make('plan')
    ->options(Plan::query()->pluck('name', 'slug')->all())
    ->required(),

Multi-select

If the field exposes multiple(), the stored value becomes an array. Cast the model attribute accordingly (protected $casts = ['tags' => 'array']) and use the same method inside ->formatStateUsing() on table columns when the value is read back.

Picker::make('tags')
    ->options(Tag::pluck('name', 'id')->all())
    ->multiple()
    ->required(),

Live-driven dependent fields

Hook the picker into Filament's reactive form system the same way you would a Select:

Picker::make('category')
    ->options(Category::pluck('name', 'id')->all())
    ->live()
    ->afterStateUpdated(fn (Set $set) => $set('subcategory', null)),

Select::make('subcategory')
    ->options(fn (Get $get) => Subcategory::where('category_id', $get('category'))->pluck('name', 'id')->all()),

Because the picker is just another form field, $get() / $set() work identically to the built-in components. That makes migration from Select mostly mechanical.

Model-backed options with rich labels

When labels need more than a string (icons, colors, thumbnails), most picker packages let you pass a closure or a custom view. Two patterns work:

  1. Keep options() as [id => label] and override the tile rendering via ->view('filament-picker::custom-tile'), passing extra data through ->extraAttributes() or a public property.
  2. Skip options() entirely and pass a pre-built array of ['value' => ..., 'label' => ..., 'icon' => ...] if the package supports structured options. Check the source for the exact shape.

Table and infolist integration

Render stored values on ListRecords and Infolist by reusing the field with ->disabled() or by formatting to the label:

Picker::make('status')
    ->options([...])
    ->disabled(),

For tables, TextColumn::make('status')->formatStateUsing(fn ($state) => $options[$state] ?? $state) often reads better than embedding a form field in a cell.

Testing with Livewire

Because the picker ultimately writes a scalar (or array) to form state, tests look exactly like Select tests:

livewire(CreatePost::class)
    ->fillForm(['status' => 'draft'])
    ->call('create')
    ->assertHasNoFormErrors();

// and read back
livewire(EditPost::class, ['record' => $post->id])
    ->assertFormSet(['status' => 'published']);

If the field's JS prevents programmatic setting, fall back to call('$form.status = "draft") — but in practice fillForm works because the underlying state is just Livewire data.

Gotchas and Tips

  • Assets are the #1 source of "it renders as a plain list". Filament packages ship compiled CSS/JS under resources/dist. After every composer update, if the layout looks wrong, run php artisan filament:assets and php artisan optimize:clear, then hard-reload the browser. Check config('filament.assets_path') and APP_URL — a trailing-slash mismatch breaks asset URLs inside SPA mode.
  • String keys, always. Livewire serializes integers and strings differently. If options() is built with pluck(), force keys to strings with ->mapWithKeys(fn ($v, $k) => [(string) $k => $v]) to avoid "saved as int, loaded as string" bugs that only show up in Edit forms.
  • Search is usually client-side. Large option sets (hundreds+) will bloat the HTML. If the package exposes getSearchResultsUsing() or searchable(), wire it to the server; otherwise filter the options query in the closure passed to options() based on $get('search').
  • ->options() closures re-evaluate on every render. Don't put heavy queries inside; cache with Cache::remember() keyed on the parent field value if the list is big.
  • Watch the Filament major version. Filament v3 and v4 have different form component contracts. Check the package's composer.json ("filament/filament": "^3.0") before adopting it in a v4 panel — the low star count means the maintainer may lag on v4 compatibility. Pin the version ("icetalker/filament-picker": "1.*") in composer.json so a patch release doesn't silently change the API.
  • Debugging a broken render: open the browser console first — a missing x-data or an Alpine ReferenceError from the picker's JS is far more common than a PHP error. Then inspect the DOM: if you see <select> markup, ->view() was overridden somewhere; if you see unstyled divs, the CSS didn't load.
  • Extension points to reach for first: ->view('vendor/...') to restyle tiles without forking; ->extraAttributes() to pass data-* for hooks; ->rules() / ->required() behave like any form component, so validation lives in your standard FormRequest or closure.
  • Don't put secrets in labels. Every option label ends up in the DOM and in Livewire's component payload. If an option's label is only needed server-side (e.g. "Internal — do not show"), filter before passing it in.
  • Upgrade path is thin. With 10 stars the package has few production battle-testers, so read the CHANGELOG between releases rather than trusting semver. If the field is critical to a form, wrap it in your own App\Forms\Components\AppPicker that extends the package class — one place to patch if the upstream API drifts, and one place to swap in your own implementation later.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager