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 Advanced Choice Laravel Package

codewithdennis/filament-advanced-choice

Eight Filament form fields that upgrade plain radio and checkbox lists into cards, stacked cards, tables, and richer lists with descriptions and extra info columns. Built on Filament's Radio and CheckboxList, so the full API still works.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and wire up the theme (do not skip step 2).

composer require codewithdennis/filament-advanced-choice

Then add the package views as a Tailwind source in your custom Filament theme CSS (resources/css/filament/admin/theme.css), rebuild, and clear caches:

@source '../../../../vendor/codewithdennis/filament-advanced-choice/resources/**/*.blade.php';
npm run build && php artisan filament:optimize-clear

The relative ../../../../ assumes the theme lives at resources/css/filament/{panel}/. If you keep your theme elsewhere, count the directories back to the project root before trusting the snippet.

Where to look first: the eight components all live in CodeWithDennis\FilamentAdvancedChoice\Filament\Forms\Components. Pick the one matching your selection semantics (single vs. multiple) first, then swap the layout later — that decision is reversible, and mixing up single/multiple is the one that forces a migration.

First use case: a plan/pricing picker on a signup or billing form. It is the shortest path from "plain radio buttons" to something presentable, and it exercises all three content slots (options(), descriptions(), extras()):

use CodeWithDennis\FilamentAdvancedChoice\Filament\Forms\Components\RadioCard;

RadioCard::make('plan')
    ->options(['hobby' => 'Hobby', 'pro' => 'Pro', 'team' => 'Team'])
    ->descriptions([
        'hobby' => 'For side projects and experiments.',
        'pro' => 'For freelancers shipping client work.',
        'team' => 'For teams that need shared billing.',
    ])
    ->extras(['hobby' => 'Free', 'pro' => '$29 / month', 'team' => '$99 / month'])
    ->default('pro')
    ->required();

If that renders as a flat list of radios, the theme did not pick up the views — go back to step 2 before debugging anything else.

Implementation Patterns

Drive options from a backed enum, not from arrays. This is the pattern worth adopting across a codebase, because every field then reads label, description, extra (and colour, for single-choice fields) from one place:

enum DeliveryType: string implements HasLabel, HasDescription, HasExtra
{
    case Standard = 'standard';
    // getLabel(), getDescription(), getExtra()
}

CheckboxStackedCard::make('delivery_type')->options(DeliveryType::class);

Keep descriptions() / extras() for one-off marketing copy on a specific form, and keep the enum for anything reused (order shipping, billing cadence, seat tiers). Explicit extras() wins over getExtra(), so an override on a single field is safe and doesn't leak into the enum's other consumers.

Choose layout by information density, not aesthetics.

Content shape Layout
Label only, short list RadioList / CheckboxList
Label + extra, many options to compare RadioTable / CheckboxTable
Label + description + extra, 3–6 options RadioCard / CheckboxCard
Long descriptions, narrow sidebar or modal RadioStackedCard / CheckboxStackedCard

Tables are the right answer for dense comparisons (shipping methods with prices, feature add-ons). Cards win when the description is doing persuasion work.

Use hiddenInputs() + hiddenInputIcon() on card layouts for the "selection is obvious" case. The whole card is clickable, so the native dot is redundant clutter:

RadioCard::make('plan')
    ->options(Plan::class)
    ->columns(3)
    ->hiddenInputs()
    ->hiddenInputIcon('heroicon-s-check-circle'); // no default icon; omitted = outline only

Because there's no default icon, a selected card with hiddenInputs() and no hiddenInputIcon() is marked only by its border. On low-contrast themes that reads as "nothing is selected" — always pass an icon.

Stack the inherited API rather than reimplementing rules. Validation (required(), in:, minItems/maxItems), searchable(), bulkToggleable() and disableOptionWhen() all behave exactly as on Radio/CheckboxList, so existing form tests and ->rules() keep working:

CheckboxCard::make('addons')
    ->options(Addon::class)
    ->bulkToggleable()
    ->minItems(1)
    ->maxItems(3)
    ->disableOptionWhen(fn (string $value): bool => $value === 'enterprise')
    ->searchable();

Make the cursor state-aware. Since v2.2.0 all eight fields default to a pointer cursor; defaultCursor() opts out and accepts a closure, which is the hook for "user cannot change this yet":

RadioCard::make('plan')
    ->options(Plan::class)
    ->defaultCursor(fn (): bool => ! auth()->user()->canChangePlan());

Pair it with disableOptionWhen() and a helper text — a card that looks unclickable but still accepts clicks is worse than one that's visibly locked.

Responsive columns as a default, not an afterthought. Cards default to three columns; on a narrow panel column that is cramped. Set it once in a shared factory method or base schema:

->columns(['default' => 1, 'md' => 2, 'xl' => 3])

gridDirection(GridDirection::Column) is worth knowing for lists of uneven length where you want visual balance top-to-bottom.

Centralise repeated configuration. If several forms show the same delivery/billing choice, wrap the field construction in a small private static method or a custom component class that pre-applies columns(), color(), hiddenInputs() and the enum. Otherwise each call site drifts, and the panel becomes inconsistent — which is exactly the problem the pointer-cursor default was added to solve.

Gotchas and Tips

The unstyled-fields trap is the number one support issue. Two distinct causes:

  • Theme never registered the @source path → plain checkbox list.
  • You upgraded the package but didn't npm run build → stale CSS, new utility classes missing.

Both present identically. When a field looks wrong, rebuild before touching PHP, and remember production deploys need the built assets, not just composer install.

RadioTable and CheckboxTable silently ignore hiddenInputs(). They always render native inputs. If you're refactoring a form from cards to a table, the hiddenInputs() call stays in the chain and does nothing — no error, just a visual surprise. Same for hiddenInputIcon(), which is card-only.

HasColor is honoured only by the four Radio fields. A per-option colour on a CheckboxCard enum case is ignored; set ->color() on the field instead. Don't build a colour-coded multi-select enum and expect it to work.

Search covers labels and descriptions, not extras. The search box matches on labels and descriptions only. If you put the meaningful discriminator in extras() (a SKU, a price band), users can't find it by typing it. If it must be searchable, fold it into the label or description.

Keep descriptions() / extras() keys aligned with options(). These are keyed arrays; a typo'd or missing key means a silently blank subtitle or extra column, not an exception. With enums this class of bug disappears, which is another argument for the enum route. When you do pass arrays, type-hint and reuse the value constants.

bulkToggleable() + maxItems(): "select all" and a maximum count express conflicting intents. Verify the resulting state on a realistic option count before shipping; if select-all can exceed the limit, drop bulkToggleable() on that field and use a helper text instead. Treat this combination as something to test rather than assume.

Accessibility with hiddenInputs(): the native input becomes a transparent element stretched over the option. That keeps it focusable and screen-reader-visible, so don't remove the label() or rely on colour alone to convey selection. Always pair hiddenInputs() with hiddenInputIcon(), and check keyboard tab/space behaviour once in a real browser.

Use the singular class names. RadioCards, RadioStackedCards, CheckboxCards, CheckboxStackedCards are deprecated aliases that still resolve. They will not fail, so they quietly accumulate in a codebase — add a grep for the plurals to your cleanup list and standardise on RadioCard, RadioStackedCard, CheckboxCard, CheckboxStackedCard.

Version and maturity posture. The package requires Filament 4.x or 5.x and is a small, single-maintainer project (no dependents, README + releases only, no test suite advertised). That is fine for form-field sugar — the components extend core Radio/CheckboxList, so the fallback is a one-line swap back to the parent class with no data migration and no state-format change. Pin the version in composer.json, and avoid subclassing or reaching into its Blade views so that fallback stays cheap. Before a major Filament upgrade, re-verify rendering rather than assuming the parent-API inheritance covers view-level changes.

Debugging checklist, in order: (1) npm run build and hard-refresh; (2) confirm the @source path actually resolves from your theme file; (3) confirm the field extends the layout you think it does (check the class, not the variable name); (4) ->live() + dd() on the field state to confirm the value is submitted as expected — state handling is inherited, so a wrong value here means the parent field would also be wrong, not the package.

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.
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
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai