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 Choosable Chips Laravel Package

vitisstudio/filament-choosable-chips

A Filament form component that lets users select one or more options as clickable chips, offering a compact visual alternative to dropdowns and integrating cleanly with Filament forms.

View on GitHub
Deep Wiki
Context7

Getting Started

Requirements check first. This package is hard-gated to PHP 8.4+ and Filament v5 (filament/forms ^5.0). Confirm both before you spend time on it — a v3/v4 Filament app or a PHP 8.2/8.3 runtime can't use it at all, and there's no backport path.

composer require vitisstudio/filament-choosable-chips

Where to look first (in this order):

  1. The field class itself — its fluent API is deliberately patterned after Select/ToggleButtons, so anything you already know about options(), defaultColor(), disableOptionWhen(), and gridDirection() transfers directly. Confirm the exact FQCN and namespace from the package's src/ or README before wiring it into a form.
  2. The single-vs-multiple state casting logic — this determines whether your field's state is a scalar or an array, which drives your migrations, casts, and validation.
  3. The Blade view for the chip/badge markup — only if you need to restyle chips or add a wrapper.

First use case: a small, enumerable choice field where you want the options visible at a glance rather than hidden behind a dropdown — status, priority, category, a small tag set, or a compact filter bar in a custom Livewire form.

use VitisStudio\FilamentChoosableChips\Chips; // verify FQCN against the package

Chips::make('status')
    ->options([
        'draft'     => 'Draft',
        'review'    => 'In review',
        'published' => 'Published',
    ])
    ->colors([
        'draft'     => 'gray',
        'review'    => 'warning',
        'published' => 'success',
    ])
    ->icons([
        'draft'     => 'heroicon-o-pencil',
        'review'    => 'heroicon-o-eye',
        'published' => 'heroicon-o-check-circle',
    ]);

That's radio semantics by default — state stays a single scalar, so it drops into an existing string column with no migration.

Implementation Patterns

Enum-backed options are the happy path. Because colors(), icons(), and descriptions() are all keyed by option value, you can drive the whole field from one enum and keep labels, colors, and icons colocated instead of drifting across three arrays:

Chips::make('status')
    ->options(Status::class)
    ->colors(collect(Status::cases())->mapWithKeys(fn ($c) => [$c->value => $c->color()])->all())
    ->icons(collect(Status::cases())->mapWithKeys(fn ($c) => [$c->value => $c->icon()])->all())
    ->required();

Choose single vs. multiple deliberately, up front. Default is radio semantics (scalar state); ->multiple() flips to checkbox semantics (array state). Treat this as a schema decision, not a styling decision — it dictates the column type, the Eloquent cast, and the shape your ->afterStateUpdated() callbacks receive.

Chips::make('tags')
    ->options(Tag::class)
    ->multiple()
    ->checkSelected();

Use the checkmark as your "selected" affordance, explicitly. ->checkSelected() turns on the selected check, and it auto-enables only when no per-option icons are set. The moment you add ->icons([...]), you lose the checkmark unless you call checkSelected() yourself. Be deterministic: call it every time you rely on it.

Gate options with disableOptionWhen() for capability, not for security. It's ideal for "you can't pick archived while the record is open" or "this option is out of stock" UX. Keep the matching authorize/policy check server-side — the field also derives an in validation rule from the enabled options, which is a strong safety net, but it's still a render-time snapshot.

Compact layouts. size(), defaultColor(), and gridDirection() are the levers for dense contexts: table filters, repeater rows, action modals, and sidebars. A horizontal grid of small chips is a good substitute for a long Select in a filter panel.

Testing. In Livewire/Filament form tests, fillForm() takes a scalar for the default mode and an array once ->multiple() is set. Write assertions against the resulting model attribute in the shape your cast produces — most test failures with this field come from shape mismatch, not from the field itself.

Gotchas and Tips

  • State shape is the #1 bug source. scalar (default) vs array (->multiple()) changes what your cast, migration, and reactive callbacks must handle. Switching a field from single to multiple after ship is a data migration (backfilling scalars into JSON arrays), not a one-line change. Decide once.
  • The derived in rule tracks the enabled options. Options filtered out by disableOptionWhen() — especially when the closure depends on another field's state — can produce validation errors that look like the field is broken. The same applies to options that only exist after a dependent field changes.
  • Adding icons silently removes the checkmark. Auto-enable of checkSelected() is conditional on there being no per-option icons. Easy to "lose" the checkmark after a cosmetic tweak.
  • The selected check replaces the dismiss button while shown. You don't get both affordances on the same chip, so don't design UX that assumes you can show "this is selected" and "click to clear" simultaneously.
  • Dismissing is a real state change. The badge delete button writes null/[] into the form state. If the field is ->required(), expect validation to fire after a user dismisses the last chip — that's correct behaviour, but it surprises people in filter contexts where "cleared" should be valid. Drop required() there.
  • Map keys must match option values by type. Colors/icons/descriptions are keyed arrays; '1' vs 1 vs an enum case will fail silently and fall back to defaultColor() with no error. If chips render but colours/icons don't apply, check key coercion first — this is the fastest debugging win.
  • Not documented in the release notes — verify before relying on: searching/filtering large option sets (chips don't scale past ~15–20 options, so keep them for small sets), tooltips on disabled options, hex/custom colors (the API appears to use Filament color token names, not arbitrary values), publishable views/assets, and reactive ->live() integration with dependent fields. Assume it's a subset of Select's surface until proven otherwise.
  • Debugging checklist: state not persisting → check the attribute cast and whether multiple() matches the column type; wrong chip styling → check map key types; validation failing on a valid-looking option → check disableOptionWhen() closures and dynamic option lists.
  • Extension points: prefer the per-option colors()/icons()/descriptions() maps and a Blade view override over forking the field class. Those are the stable, cheap surface; a fork will need re-merging on every Filament v5 minor bump.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core