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

Nova Radio Field Laravel Package

owenmelbz/nova-radio-field

A Laravel Nova field that renders options as radio buttons instead of a dropdown, giving a clearer single-choice selection UI. Configure it with the same options array you'd use for Nova's built-in select fields, and it drops straight into resource forms with no extra setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer — Nova auto-discovers the field, so there's no service provider, config publish, or asset publish step:

composer require owenmelbz/nova-radio-field

Before touching a resource, spend ten minutes on a compatibility check. The last release was July 2021, which predates Nova 4's Vite asset pipeline and Vue 3. Run composer show owenmelbz/nova-radio-field -a to see the laravel/nova constraint it declares, then open vendor/owenmelbz/nova-radio-field/ and look at three files in this order: the PHP Radio class (tiny — options() and toggle() are the whole API), composer.json, and dist/js/*. The README won't tell you whether it works on your Nova version; those three will get you most of the way, and a five-minute render test settles it.

The first use case is any small enum you already have as a Select. Open app/Nova/Post.php and swap it:

use OwenMelbz\RadioField\Radio;

Radio::make('Status')
    ->options([
        'draft'     => 'Draft',
        'published' => 'Published',
        'archived'  => 'Archived',
    ])
    ->default('draft')
    ->rules('required');

Then prove the three things that actually matter in one sitting, on one throwaway resource:

  1. Render — does the radio group appear inline, or is the field blank/erroring in the console?
  2. Persistence — does the option key save on create and update? Check the DB, not just the form.
  3. Toggling — wire ->toggle(['published' => ['publish_date']]) and confirm the target hides and shows.

If render fails, stop there. You're on an unsupported Nova, and the fix is forking the Vue component into nova-components/, not working around it in resource code.

Implementation Patterns

The toggle map is the reason you're adopting this. Map option values to arrays of field attribute names — the first argument to make(), not the label:

Radio::make('Status')
    ->options(['draft' => 'Draft', 'published' => 'Published', 'archived' => 'Archived'])
    ->toggle([
        'published' => ['publish_date', 'featured_image', 'author_id'],
        'archived'  => ['archived_at', 'archive_reason'],
    ])
    ->rules('required');

The targets must exist in the same fields() array. The frontend resolves them by attribute name at runtime, so a rename in either place silently breaks the pairing.

Treat it as an ordinary Nova field. It subclasses Laravel\Nova\Fields\Field, so ->help(), ->nullable(), ->sortable(), ->hideFromIndex(), ->onlyOnForms(), ->resolveUsing(), and ->fillUsing() all compose as usual. Persistence behaves exactly like a Select — the option key is stored verbatim — so unless you override with resolveUsing/fillUsing, there's nothing new to learn about storage. Keep ->options() last in the chain for readability; there's no ordering requirement, it's just easier to scan.

Centralize repeated status blocks in a factory. The moment a second resource needs the same radio-plus-toggle, extract it — otherwise the toggle map drifts and you get inconsistent visibility across panels:

class PublishStatusRadio
{
    public static function make(string $attribute = 'status'): Radio
    {
        return Radio::make($attribute)
            ->options(['draft' => 'Draft', 'published' => 'Published', 'archived' => 'Archived'])
            ->toggle([
                'published' => ['publish_date', 'featured_image', 'author_id'],
                'archived'  => ['archived_at', 'archive_reason'],
            ])
            ->default('draft')
            ->rules('required');
    }
}

Because the fluent API returns $this, callers can still layer on ->help('...') or ->sortable() per resource. This factory is also the single place to edit when the option set changes — a real win when three resources share a workflow.

Pair a default with the toggle. The "draft by default, reveal publishing fields only on Published" flow is the canonical pattern and the one most likely to justify the package over a plain Select:

PublishStatusRadio::make()
    ->toggle([...])
    ->default('draft'),

New records land in the safe state and the form stays short until the user makes an explicit choice.

Split responsibilities with native dependsOn(). Nova 3.14+ ships value-driven visibility. Use dependsOn() for chained or computed dependencies (field B depends on field A depends on field C) and reserve ->toggle() for the flat "one radio drives a fixed set of siblings" case. Critically, never point both mechanisms at the same target field — they fight over the same visibility style and the last render wins.

Keep it form-side. ->toggle() is a form-view behaviour only. On index and detail the radio renders as its plain value and the toggle map is ignored. If children shouldn't appear on detail, use their own ->hideFromDetail(); don't expect the radio to govern that.

Fork the Vue component when you need custom markup. Per-option descriptions, icons, or inline help won't reach the render path through the field's meta bag. The component is one file — copy resources/js/components/Radio.vue into your own Nova component and own the build. That's a smaller job than it sounds and it's the natural escape hatch.

Gotchas and Tips

  • Nova 4/5 support is the headline risk, not a footnote. The 2021 release predates Nova 4's Vite pipeline and Vue 3. The PHP class usually survives; the compiled bundle is the fragile artifact and may render blank or throw inside the form's shared Vue tree without a build error. Gate adoption on a throwaway-branch spike against your exact production Nova version, and assume "fork and own the build" is the outcome rather than "adopt and rely on upstream."
  • Integer option keys break toggling. Nova stringifies values in the JSON it sends to the frontend, so ->toggle([1 => [...]]) won't reliably match when the user selects 1. Use string keys ('yes', 'draft', 'published') or cast on resolve. This bites hardest when you also compare against dependsOn callbacks or index filters.
  • Toggled targets must be registered in the form tree. Conditionally omitting a child with ->hideWhenUpdating() or a closure that returns nothing can leave the radio's JS unable to resolve its target — usually a console error, sometimes a silent no-op. Prefer ->onlyOnForms() / ->hideFromIndex() on the child over conditional registration.
  • Two radios targeting the same field conflict. The handler overwrites the target's visibility style and the last render wins, so behaviour depends on field order. Consolidate to a single radio or drive the second level with native dependsOn().
  • Debugging "the field won't show" starts in the browser console. A renamed attribute, a missing target, or a Nova build that never loaded the package's JS all fail silently from PHP's perspective. A deliberate smoke test — ->toggle(['published' => ['does_not_exist']]) — tells you whether the listener is wired at all: if you see a lookup error, the JS is running; if nothing happens, the bundle isn't loading.
  • ->toggle() doing nothing on index/detail is expected. It's form behaviour. Don't file a bug.
  • Values persist exactly like a Select. No surprises, but also no magic — if you need to transform the stored value, layer ->resolveUsing() / ->fillUsing() as you would with any Nova field.
  • Verify keyboard accessibility yourself. Confirm the rendered control is a real radio group (keyboard-navigable, screen-reader-announced), not styled divs with click handlers. The source is the only answer; test with Tab and arrow keys on the spike branch before shipping to anyone who relies on them.
  • Pin the version and assign an owner. Single maintainer, ~4 years without a release, zero dependents. Add the fork/bundle to the checklist for every Nova major upgrade. Blast radius is one authenticated admin panel — a failure degrades a form, not the app — so pilot on one low-traffic resource before spreading it across the codebase.
  • Don't reach for it outside admin. This is a presentation convenience for internal, authenticated Nova screens. It is not a candidate for customer-facing forms, multi-step conditional workflows, or anything requiring vendor support, and it's the wrong tool if the toggle rules are complex or will be copy-pasted without a shared factory.
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.
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
nodisrupt/laravel-agent
sierratecnologia/media-manager