Radio Deck by Jay-Are Ocero
CommunityTurn Filament's radio and checkbox list into selectable cards with icons, descriptions, colors, search and relationships.
Author:
Jay-Are Ocero
Package health
Automated checks of this plugin's Composer package
15 checks
- Skipped: GitHub Actions pinned to SHA
- Skipped: GitLab CI includes pinned to SHA
- Passed: Open security advisories
- Passed: Dependabot PR responsiveness — No open Dependabot PRs.
- Skipped: Renovate MR responsiveness
- Skipped: Dependabot or Renovate configured
- Skipped: Dependency update cooldown configured
- Failed: Provides a security policy — View details on Plumb
- Passed: Abandoned or archived — No consulted source marks the package abandoned (packagist).
- Warning: Commit and release recency — Low activity: no commit date available; last release 206 days ago.
-
Passed:
composer.lock not committed by library
—
composer.lockis absent from the released dist archive. - Passed: Dist archive is lean
-
Passed:
Current Laravel version supported
—
Package dependencies resolve together with current Laravel
13.0. -
Passed:
Current PHP version supported
—
Constraint
^8.2supports current PHP8.5. - Skipped: Current Symfony version supported
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Documentation
- Requirements
- Installation
- Usage
- Upgrading
- Testing
- Changelog
- Contributing
- Security Vulnerabilities
- Credits
- License

Turn Filament's radio and checkbox list into selectable cards with icons, titles, descriptions and colors.
#Requirements
- PHP 8.2+
- Laravel 11.28+
- Filament v4.x or v5.x
- A custom Filament theme (Tailwind CSS v4)
#Installation
composer require jaocero/radio-deck
Add the plugin's views to your custom theme's CSS file (usually resources/css/filament/admin/theme.css), then rebuild your assets:
@source '../../../../vendor/jaocero/radio-deck/resources/views';
Publish the plugin's JavaScript (used by searchable() and bulkToggleable()):
php artisan filament:assets
#Usage
use Filament\Schemas\Schema;
use Filament\Support\Enums\Alignment;
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
use Filament\Support\Icons\Heroicon;
use JaOcero\RadioDeck\Forms\Components\RadioDeck;
public static function configure(Schema $schema): Schema
{
return $schema
->components([
RadioDeck::make('platform')
->options([
'ios' => 'iOS',
'android' => 'Android',
'web' => 'Web',
])
->descriptions([
'ios' => 'iOS mobile app',
'android' => 'Android mobile app',
'web' => 'Web app',
])
->icons([
'ios' => Heroicon::OutlinedDevicePhoneMobile,
'android' => 'heroicon-o-device-phone-mobile',
'web' => Heroicon::OutlinedGlobeAlt,
])
->required()
->iconSize(IconSize::Medium) // ExtraSmall | Small | Medium | Large | ExtraLarge | TwoExtraLarge, or classes like 'size-12'
->iconPosition(IconPosition::Before) // Before | After
->alignment(Alignment::Center) // Start | Center | End
->optionsGap('gap-5') // Gap between the icon and the text inside a card
->padding('px-4 py-6') // Padding inside each card
->extraCardsAttributes(['class' => 'rounded-xl'])
->extraOptionsAttributes(['class' => 'text-lg'])
->extraDescriptionsAttributes(['class' => 'text-sm font-light'])
->color('primary') // Color of every option
->columns(3), // or ['sm' => 1, 'md' => 2, 'lg' => 3]
]);
}
#Colors
Use one color for every option, or a color per option. Colors can be names registered with Filament or palettes from Filament\Support\Colors\Color:
use Filament\Support\Colors\Color;
RadioDeck::make('plan')
->options([...])
->colors([
'hobby' => 'gray',
'pro' => Color::Violet,
'team' => Color::hex('#0ea5e9'),
])
color() is only the fallback for options without their own color. A color from colors(), the options enum's HasColor, or getOptionColorFromRecordUsing() always wins. To force one color on every option, pass it to colors() instead:
RadioDeck::make('platform')
->options(Platform::class)
->colors('danger') // Overrides the enum's colors
#Using enums
Pass an enum to options() and RadioDeck reads labels, descriptions, icons and colors from Filament's own enum contracts. The selected value is cast to the enum.
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasDescription;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Icons\Heroicon;
enum Platform: string implements HasColor, HasDescription, HasIcon, HasLabel
{
case Ios = 'ios';
case Web = 'web';
public function getLabel(): string
{
return match ($this) {
self::Ios => 'iOS',
self::Web => 'Web',
};
}
public function getDescription(): string
{
return match ($this) {
self::Ios => 'iOS mobile app',
self::Web => 'Web app',
};
}
public function getIcon(): Heroicon
{
return match ($this) {
self::Ios => Heroicon::OutlinedDevicePhoneMobile,
self::Web => Heroicon::OutlinedGlobeAlt,
};
}
public function getColor(): string
{
return match ($this) {
self::Ios => 'gray',
self::Web => 'info',
};
}
}
RadioDeck::make('platform')
->options(Platform::class)
Explicit descriptions(), icons() and colors() arrays still take priority over the enum.
The
JaOcero\RadioDeck\Contracts\HasDescriptionsandHasIconscontracts (with->descriptions(Enum::class)and->icons(Enum::class)) still work, but are deprecated. Implement Filament'sHasDescriptionandHasIconinstead.
#Multiple selection
multiple() renders checkboxes and stores an array. Cast the attribute to array (or an enum collection) on your model.
RadioDeck::make('regions')
->options([...])
->multiple()
->minItems(1)
->maxItems(3)
#Searching and bulk toggling
RadioDeck::make('regions')
->options([...])
->multiple()
->searchable()
->bulkToggleable() // "Select all" and "Deselect all", multiple() only
->searchPrompt('Search regions')
->noSearchResultsMessage('No regions found.')
->searchDebounce(300)
Customize the bulk toggle actions with selectAllAction(fn (Action $action) => $action->label(...)) and deselectAllAction().
#Integrating with an Eloquent relationship
For a BelongsTo relationship, name the field after the foreign key:
use App\Models\Category;
RadioDeck::make('category_id')
->relationship('category', 'name')
->getOptionDescriptionFromRecordUsing(fn (Category $record): ?string => $record->description)
->getOptionIconFromRecordUsing(fn (Category $record): ?string => $record->icon)
->getOptionColorFromRecordUsing(fn (Category $record): ?string => $record->color)
For a BelongsToMany relationship, enable multiple(). The selection is synced to the pivot table, and pivotData() is saved with it:
RadioDeck::make('features')
->relationship(titleAttribute: 'name')
->multiple()
->pivotData(['note' => 'Added by an admin'])
relationship() also accepts a modifyQueryUsing closure, and getOptionLabelFromRecordUsing() customizes the labels.
#Boolean options
RadioDeck::make('is_public')
->boolean('Public', 'Private')
->icons([1 => Heroicon::OutlinedLockOpen, 0 => Heroicon::OutlinedLockClosed])
#Inline layout
inline() places the cards next to each other instead of in a grid:
RadioDeck::make('size')
->options([...])
->inline()
In a grid, cards fill rows first. gridDirection(GridDirection::Column) fills columns first instead.
#Tooltips
RadioDeck::make('plan')
->options([...])
->tooltips([
'enterprise' => 'Contact sales to unlock this plan.',
])
#Selection indicator
Show a radio or checkbox style indicator in the corner of each card:
RadioDeck::make('plan')
->options([...])
->selectionIndicator()
The indicator follows alignment(): vertically centered by default, at the top with Alignment::Start and at the bottom with Alignment::End. Stacked cards (with a flex-col class) keep it in the top corner. With the default padding, cards reserve space on the right for the indicator. If you set your own padding(), it's used as-is, so keep it symmetric for centered tiles.
#Disabling options
RadioDeck::make('plan')
->options([...])
->disableOptionWhen(fn (string $value): bool => $value === 'enterprise')
Submitted values are validated against the enabled options automatically.
#Styling
Every part of the card has a stable CSS class you can target from your theme: fi-fo-radio-deck, fi-fo-radio-deck-option, fi-fo-radio-deck-card, fi-fo-radio-deck-icon, fi-fo-radio-deck-label, fi-fo-radio-deck-description and fi-fo-radio-deck-indicator.
#Upgrading
#From v1.x (Filament v3)
- Create a custom theme with
php artisan make:filament-theme. - Remove the plugin from the
contentarray of yourtailwind.config.jsand add the@sourcedirective shown above to your theme CSS. - Rename
->gap()to->optionsGap().
#Deprecated enum contracts
Replace JaOcero\RadioDeck\Contracts\HasDescriptions::getDescriptions() and HasIcons::getIcons() with Filament's HasDescription::getDescription() and HasIcon::getIcon(). You can then drop the ->descriptions(Enum::class) and ->icons(Enum::class) calls, because ->options(Enum::class) picks them up.
#Testing
composer test
#Changelog
Please see CHANGELOG for more information on what has changed recently.
#Contributing
Please see CONTRIBUTING for details.
#Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
#Credits
#License
The MIT License (MIT). Please see License File for more information.
The author
Jay-Are Ocero is a full stack developer and UI/UX designer who always been passionate about web development.
From the same author
Featured Plugins
A selection of plugins curated by the Filament team
Custom Dashboards
Let your users build and share their own dashboards with a drag-and-drop interface. Define your data sources in PHP and let them do the rest.
Filament
Spotlight Pro
Browse your Filament Panel with ease. Filament Spotlight Pro adds a Spotlight like Command Palette to your Filament Panel.
Dennis Koch
Soft Theme
A theme that gives panels a warm, approachable look with rounded shapes, gentle colors, and serif headings.
Filament