naif/toggle-switch-field
A Laravel Nova field that renders a clean, interactive toggle switch for boolean attributes, making on/off settings easier to scan and edit in your admin panel. Simple to install and configure, it replaces default checkboxes with a polished switch UI.
What it is: a Laravel Nova field package that renders a boolean attribute as an on/off toggle switch instead of Nova's default checkbox. It is a thin UI wrapper — the storage layer is still a normal boolean column on your model.
Requirements to check before installing
composer.json constraint.Install
composer require naif/toggle-switch-field
Field packages normally register their compiled JS through a FieldServiceProvider and need no manual publish. If the README asks for it, publish assets and rebuild:
php artisan vendor:publish --tag=toggle-switch-field-assets
Where to look first (in priority order)
README.md — the canonical usage snippet and the exact field class FQCN.src/ — find the field class. Check what it extends: if it extends Laravel\Nova\Fields\Boolean you inherit the entire Boolean API (trueValue, falseValue, nullable, resolveUsing) for free. That single detail determines how much of the patterns below apply.resources/js/ — the Vue component. Confirms which props (trueLabel, falseLabel, colors, sizes) the field actually supports.config/ — if present, tells you whether labels/colors are configurable globally vs. per-field.First use case
You have a users.is_active column and a Nova User resource where admins flip accounts on and off several times a day. Replace the checkbox:
use Naif\ToggleSwitchField\ToggleSwitchField; // verify FQCN in README
public function fields(NovaRequest $request): array
{
return [
ID::make()->sortable(),
Text::make('Name')->rules('required', 'max:255'),
ToggleSwitchField::make('Active', 'is_active')
->default(true)
->rules('boolean'),
];
}
Migrate and cast so the value round-trips cleanly:
// migration
$table->boolean('is_active')->default(true)->index();
// app/Models/User.php
protected $casts = ['is_active' => 'boolean'];
The ->index() matters more than it looks — the whole point of a toggle is fast filtering, and you'll end up writing User::where('is_active', true) constantly.
1. The one-line upgrade path from Boolean
If the field extends Nova's Boolean, adoption is a single line per field. That's the intended workflow: treat it as a drop-in skin, keep the rest of the field configuration identical.
// before
Boolean::make('Active', 'is_active')->rules('boolean'),
// after
ToggleSwitchField::make('Active', 'is_active')->rules('boolean'),
2. Forms only, read-only on detail
Toggles are for the edit form. On index/detail, a plain text badge often reads better in dense tables.
ToggleSwitchField::make('Active', 'is_active')
->onlyOnForms()
->rules('boolean'),
Boolean::make('Active', 'is_active') // renders "Yes/No" on index & detail
->exceptOnForms(),
3. Feature-flag style usage
This is where toggles earn their keep — a resource full of booleans that read as a control panel:
Panel::make('Feature Flags', [
ToggleSwitchField::make('Beta Access', 'flag_beta'),
ToggleSwitchField::make('New Dashboard', 'flag_dashboard'),
ToggleSwitchField::make('Email Digest', 'flag_digest'),
]),
Group them in one Panel so the whole block saves in a single update request.
4. Coupling state to a timestamp
A boolean that implies a date is a classic source of drift. Use fillUsing to keep them in sync:
ToggleSwitchField::make('Active', 'is_active')
->fillUsing(function ($request, $model, $attribute, $requestAttribute) {
$value = (bool) $request->input($requestAttribute);
$model->{$attribute} = $value;
if ($value && ! $model->activated_at) {
$model->activated_at = now();
}
}),
5. Guarding destructive flips
Deactivating an account is not the same risk as renaming it. Gate it:
ToggleSwitchField::make('Active', 'is_active')
->canSee(fn (NovaRequest $request) => $request->user()->isAdmin())
->hideWhenUpdating(fn (NovaRequest $request) => $request->user()->id === $request->resourceId),
Note hideWhenUpdating on a field you also rely on for filtering is a footgun — decide whether the field should be invisible or read-only (->readonly()) instead.
6. Validation is still your job
The toggle controls the widget, not the input. A crafted request can post is_active="drop table".
ToggleSwitchField::make('Active', 'is_active')->rules('required', 'boolean'),
7. Testing the toggle
The Vue component is irrelevant to your test suite — assert at the HTTP layer:
public function test_admin_can_deactivate_user(): void
{
$this->actingAs($this->admin)
->putJson("/nova-api/users/{$this->user->id}", ['is_active' => false])
->assertOk();
$this->assertFalse($this->user->fresh()->is_active);
}
If you want a unit-level guard against someone silently swapping the field, assert the field instance type in a Nova resource test:
$this->assertInstanceOf(ToggleSwitchField::class, (new User)->fields(NovaRequest::create('/'))[2]);
It's a small package — plan for the bus factor. Six stars and zero dependents means no community pressure to fix Nova upgrades. Two mitigations: pin the exact version in composer.json, and wrap usage in your own field subclass so swapping back to Boolean is a one-file change.
namespace App\Nova\Fields;
class Toggle extends \Naif\ToggleSwitchField\ToggleSwitchField
{
public function component()
{
return 'toggle-switch-field'; // override here if you ever fork it
}
}
The toggle renders as a plain checkbox. That means the JS component failed to load, not that the field is broken. Check, in order: the browser console for a 404 on the component chunk; whether nova:publish (or the package's asset tag) was actually run in this environment; whether your public/vendor/ directory survived your deploy (common on containerized/Forge deploys where build artifacts are gitignored). Re-run the publish step as part of your deploy script.
Nova already has a toggle-ish Boolean field. Before adding a dependency, confirm the value is real — custom on/off labels, a specific size, brand colors. If you just want a nicer boolean, Nova's built-in Boolean in Nova 4/5 is already serviceable. The honest cost/benefit here is one more package in your composer.json for a UI nicety.
Tri-state is not a toggle. nullable booleans (true/false/null, where null means "not yet decided") do not map to a two-position switch. If you have null semantics, use a Select with three options. Toggles silently coerce null → false on save and you'll lose the distinction.
MySQL tinyint(1) vs Postgres boolean. On Postgres, posting the string "false" to a boolean column throws. Cast on the model ('is_active' => 'boolean') and validate with the boolean rule so the request payload is normalized before it reaches the driver.
Labels are the accessibility story. A bare switch conveys nothing to a screen reader without an associated label. If the package exposes label props, use them; otherwise put the field in a Panel with a heading so context is never purely visual. Also sanity-check dark mode — small packages rarely test it, and a switch with a transparent track disappears entirely.
Inline editing on the index is generally not supported. If rapid toggling from the index is your actual requirement, that's a different feature — use a Nova Action (ToggleActive) or Nova 4's inline edit on a Boolean, not this field.
Upgrading Nova will break this before it breaks anything else. After any Nova major bump, run the package's publish/rebuild step and click every toggle in staging. If there's no compatible release, the escape hatch is subclassing Nova's Boolean and shipping your own three-line Vue component — realistically that's less work than the dependency is saving you, which is worth knowing before you spread the field across twenty resources.
How can I help you explore Laravel packages today?