suleymanozev/filament-radio-button-field
A lightweight Filament form field for rendering radio buttons with labels, descriptions, and validation support. Drop it into any Filament form to give users clear, accessible single-choice inputs in inline or stacked layouts.
Install
composer require suleymanozev/filament-radio-button-field
No service provider registration is needed beyond Laravel package discovery. If the package ships published views you'll see a vendor:publish tag in its README — check vendor/suleymanozev/filament-radio-button-field/README.md first, since the field is view-backed and any CSS/Blade overrides live there.
Where to look first
vendor/suleymanozev/filament-radio-button-field/src/ — usually a single Forms/Components/RadioButton.php class. Read it before using it: it tells you whether it extends Filament's Field or Radio, which determines which methods (e.g. inline(), boolean()) you can chain.composer.json of the package — confirm the Filament constraint (v3 vs v4). At 36 stars this package moves with Filament's majors slowly, so verify before upgrading Filament.<input type="radio"> markup and Tailwind classes are, and it's your override point.First use case
A status/priority selector on a resource form is the canonical use:
use Suleymanozev\FilamentRadioButtonField\Forms\Components\RadioButton;
RadioButton::make('status')
->options([
'draft' => 'Draft',
'review' => 'In review',
'published' => 'Published',
])
->default('draft')
->required();
Render it, confirm state saves, then start layering layout and reactivity.
Options from an enum
Preferred over hard-coded arrays so validation stays in sync:
RadioButton::make('status')
->options(StatusEnum::class)
->required();
Filament resolves backed enums to value => label automatically; the field stores the scalar backing value, so your cast on the model keeps working.
Layout: inline pills vs stacked list
Because the field renders its own markup, layout is controlled through the component rather than a ->inline() call unless the package explicitly exposes it. Check the class first; if inline() / columns() are inherited from Filament's Field, they may be no-ops on a custom view. The reliable path is a Grid or Section wrapper with a narrow columnSpan:
Section::make('Visibility')
->schema([
RadioButton::make('visibility')
->options(['public' => 'Public', 'private' => 'Private'])
->columnSpan(1),
])
->columns(2);
Reactive forms (the main reason people reach for a radio over a Select)
RadioButton::make('billing_type')
->options(['card' => 'Card', 'invoice' => 'Invoice'])
->live()
->afterStateUpdated(fn (Set $set) => $set('card_number', null)),
TextInput::make('card_number')
->visible(fn (Get $get) => $get('billing_type') === 'card'),
->live() is mandatory — without it the dependent field's visible() closure never re-evaluates.
Conditional disabling / readonly
Use closures so the field reacts to record state on edit pages:
RadioButton::make('role')
->options(['member' => 'Member', 'admin' => 'Admin'])
->disabled(fn (?User $record) => $record?->is(auth()->user()) ?? false);
In modals and custom Livewire forms
The field is a standard Filament form component, so it works unchanged inside Action::make()->form([...]) and inside a HasForms Livewire component with your own $this->form. That's the easiest way to preview styling without touching a resource.
Table filters
This package is forms-only. For filtering a table by the same enum, use SelectFilter — don't try to reuse the component.
Filament\Forms\Components\Radio. If you import this package's class with the same alias you'll get confusing "method does not exist" errors. Alias it (use ... as RadioButtonField;) or rely on the differing class name and never import both unaliased.RadioButton extends Field rather than Radio, methods like inline(), columns(), boolean(), and descriptions() are absent — calls succeed at the PHP level only if the package defines __call passthrough, which it likely doesn't. Inspect the class before chaining anything beyond universal field methods (label, default, required, disabled, helperText, live, rule).['0' => 'No', '1' => 'Yes'] may round-trip to '1' while your model casts to int, so === comparisons in visible() closures fail silently. Prefer string keys, or compare loosely / (int) cast.options() the way a Select with in: does in every Filament version. Add ->in(array_keys($options)) on anything user-tamperable (a malicious Livewire payload can set any string).<input type="radio"> with a <label> carrying a matching for/name. Custom-styled radios frequently drop the input visually (opacity-0) and lose keyboard focus. Tab into the field once before shipping.livewire(CreatePost::class)->fillForm(['status' => 'published'])->assertFormSet(['status' => 'published']), and assertHasFormErrors(['status' => 'required']) for the empty case. Avoid asserting on the DOM class names — they're the most likely thing to change between releases.composer.json and keep a fallback in mind: Radio::make(...)->inline() or a Select covers 90% of the same use cases if the package breaks against a future Filament major. The extension point worth knowing is the field's own view — publishing and editing it is a cheaper fix than forking the package.How can I help you explore Laravel packages today?