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 Toggle Laravel Package

davidpiesse/nova-toggle

Add a boolean toggle switch field to Laravel Nova resources with davidpiesse/nova-toggle. Display on/off states as a compact, interactive switch in forms and index views, perfect for flags, active status, and simple true/false settings.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you type composer require. This package is a Nova field and nothing else — no Blade directive, no Livewire component, no standalone JS. If you aren't running Laravel Nova, close the tab. If you are, the next question is which Nova: the last release is 2022-07-22, which puts it in Nova 4 / Vue 2 territory. Nova 5's Vue 3 + Vite pipeline is the documented failure mode and it fails silently. Confirm your major version first.

composer require davidpiesse/nova-toggle

Then inspect what landed before writing a single field. This is a ~100-line package; five minutes of reading saves you a debugging afternoon:

cat vendor/davidpiesse/nova-toggle/composer.json  # explicit laravel/nova constraint, or wildcard?
ls  vendor/davidpiesse/nova-toggle/src            # Toggle.php + service provider
ls  vendor/davidpiesse/nova-toggle/dist           # prebuilt bundle committed? Vue 2 or Vue 3?

Read src/Toggle.php first and confirm it extends Boolean — not a reimplementation. If it extends Boolean, you inherit default(), rules(), nullable(), trueValue(), falseValue(), fillUsing(), onlyOnForms(), exceptOnForms() for free, and field semantics stay identical to core. If it reinvents them, stop and reconsider. Second, check the provider's boot() for Nova::script() / Nova::style() calls naming the bundle. Third, look at dist/ — whether the compiled asset exists and which Vue major it targets is the single answer that decides adopt-versus-fork.

First use case. Pilot on one non-critical resource — a settings page, a taxonomy table, a CMS flag — never on users.is_active first. Swap exactly one field:

// app/Nova/Setting.php
use Davidpiesse\NovaToggle\Toggle;

public function fields(NovaRequest $request)
{
    return [
        // Boolean::make('Published', 'is_published'),
        Toggle::make('Published', 'is_published'),
    ];
}

No migration, no schema change, no data remediation. The column stays boolean/tinyint(1). Rollback is reverting the import and the make() call — which is why the pilot is cheap and why you should never bundle the swap with a schema or policy change in the same PR.

Verify all four surfaces before rolling out further: index cell, detail view, create form, update form. Then, if you use inline index editing, verify that too — it's the surface most likely to break, because it exercises the component's update path rather than just its render path. Confirm persistence on create and update, and confirm default() and rules() behave exactly as Boolean did. Open DevTools → Console while you do this: a broken Vue mount produces a 200, a clean log, and an empty field.


Implementation Patterns

The straight swap, resource by resource

The realistic day-to-day work here is mechanical: grep -r "Boolean::make" app/Nova, classify each field, swap, one PR per resource. Keep a written inventory of which fields are now toggles — that inventory becomes your bounded QA checklist on every future Nova upgrade.

Toggle::make('Featured', 'is_featured'),
Toggle::make('Verified', 'is_verified'),

The discipline that matters: the toggle is a view layer only. Never let business logic live in the field definition. Audit trails, permission gates, and optimistic rollback belong in the model, an observer, or a Nova action:

// app/Observers/ProductObserver.php — audit trail lives here, not in the field
public function updated(Product $product): void
{
    if ($product->wasChanged('is_published')) {
        ActivityLog::record('product.published', $product, [
            'from' => $product->getOriginal('is_published'),
            'to'   => $product->is_published,
        ]);
    }
}

Inline index toggling makes accidents cheaper but not less likely — a misplaced click on a listing now flips is_published in production. That's precisely why the observer above exists. Policies still gate the request; human risk is what changed.

Centralising the field definition

If you have dozens of boolean flags, don't scatter Toggle::make() calls across resources. A small factory keeps labels and colors consistent and gives you a one-line kill switch back to Boolean:

// app/Nova/Fields/Flag.php
namespace App\Nova\Fields;

use Davidpiesse\NovaToggle\Toggle;
use Laravel\Nova\Fields\Boolean;

class Flag
{
    public static function make(string $name, ?string $attribute = null)
    {
        if (! config('nova.toggles_enabled')) {
            return Boolean::make($name, $attribute ?? $name);
        }

        return Toggle::make($name, $attribute ?? $name);
        // README documents on/off labels and colors — verify exact method names
        // against src/Toggle.php before chaining, e.g. ->onColor(...)->offColor(...)
    }
}

This is the highest-leverage pattern in the package: it gives you a config-flag rollback to plain checkboxes without touching every resource, which is exactly what you want if a Nova minor upgrade breaks the Vue component mid-sprint.

Scoping to the surfaces you actually want

Because the class extends Boolean, Nova's inherited scoping methods work normally:

Toggle::make('Published', 'is_published')
    ->exceptOnForms(),       // index/detail only — flip without opening the form

Toggle::make('Locked', 'is_locked')
    ->onlyOnDetail()
    ->help('Locked accounts cannot sign in.'),

exceptOnForms() on the index is the pattern that actually delivers the package's stated value: ops staff flipping a flag straight from the list view. Be honest with yourself — if your team never edits from the index, the marginal benefit over Boolean drops sharply and a checkbox is fine.

Keeping Boolean where it's the right tool

Don't sweep blindly. Retain Boolean for:

  • Nullable / tri-state attributes — nullable() with a meaningful "unset" state is a checkbox-with-indeterminate problem, not a two-position switch problem. Keep Boolean until you've explicitly tested null round-tripping through the toggle.
  • Non-standard trueValue() / falseValue() — e.g. 'yes'/'no' or 1/0 against a string column. Verify inheritance before assuming it works.
  • Fields where the affordance is genuinely a form answer, not a live operational state.

Testing and QA

Automated tests won't catch a broken Vue component. What you can assert server-side is persistence and authorization:

it('persists the toggle attribute on update', function () {
    $product = Product::factory()->create(['is_featured' => false]);

    $this->actingAs($admin)
        ->putJson("/nova-api/products/{$product->id}", ['is_featured' => true])
        ->assertOk();

    expect($product->fresh()->is_featured)->toBeTrue();
});

That proves persistence and policy — not rendering. Rendering requires a visual QA step in the release checklist. A blank field, a raw input, or an empty cell all render with a 200 and zero server-side error.

Sequencing a full rollout

  1. Inventory and classify boolean fields in app/Nova.
  2. Pilot one non-critical resource in staging.
  3. Verify four surfaces + inline index editing, with the console open.
  4. Confirm default() / rules() parity with Boolean.
  5. Roll out per-resource under normal PR review.
  6. Register the package on the Nova major-upgrade checklist with a named owner.
  7. Pre-authorise the fork path: promote the PHP class and Vue component into an internal acme/nova-fields package.

Gotchas and Tips

Nova 5 / Vue 3 is the real risk, not code quality. A dist/ compiled for Vue 2 mounted into a Vue 3 Nova produces an empty field with no server-side error — the worst failure mode, because logs are clean and data is intact. A silent console error at component registration is your only signal. Open the browser console on the pilot page; don't rely on the test suite or on storage/logs.

Pin the exact version and record the validated Nova version in the repo. "davidpiesse/nova-toggle": "1.2.3" — not ^1.0. Write the validated Nova version into docs/nova.md or a composer comment, because the person doing the next Nova upgrade will not know this package is load-bearing.

An absent or wildcarded laravel/nova constraint is a red flag, not a convenience. It means the package never declared what it works with. Escalate that to a deliberate "pin and pilot" decision rather than accepting it.

Prebuilt dist/ vs. building from source. If a compiled bundle is committed, you get a free pass — unless you need to patch behaviour, at which point you inherit a permanent build obligation against your own Nova asset pipeline. Decide that before adopting, not after. If you must build, budget CI time alongside the fork.

Beware stale published assets. php artisan vendor:publish / nova:publish copies can shadow the vendor bundle and mask an upgrade. If the field looks right before a Nova bump and broken after, check for a stray published copy under public/vendor/ before debugging the package.

Component-name collisions. Nova field packages register Vue components by name. Install another toggle-ish field and two registrations under the same name will silently win-or-lose depending on boot order. If you fork, namespace the component name.

Treat an unusable is_active toggle as an outage, not a cosmetic bug. If ops can't reach the control, a live workflow is blocked. Route detection through visual QA, and keep the field inventory so you know exactly which workflows to spot-check after any Nova change.

Debugging checklist when the field renders blank:

# 1. Is the asset being served at all?
curl -I https://yourapp.test/vendor/nova-toggle/js/field.js

# 2. Did the service provider actually boot?
php artisan about | grep -i toggle

# 3. Rebuild Nova's asset manifest / clear published caches
php artisan nova:publish --force && php artisan view:clear

Then DevTools → Console on the resource index. [Vue warn]: Failed to resolve component or an ES-module syntax error both point at a Vue-major mismatch, not at your field configuration.

Assume unmaintained on day one. Single maintainer, no release since 2022, zero dependents, community-only support — Nova's paid support does not extend to third-party fields. Write the fork procedure and the escalation contact into the adoption record now, so the decision isn't invented mid-incident. Given the size — one PHP class, one Vue component, one asset — forking is hours, not weeks.

Extension points, in order of preference: (1) chain Nova's inherited field methods; (2) subclass Toggle in app/Nova/Fields/ to add your own config helpers; (3) fork into an internal acme/nova-fields package if you need to patch the Vue component or when Nova's next major breaks it. Option 3 removes single-maintainer exposure permanently and is the sane endgame for a hundred lines of code you'd otherwise re-verify on every Nova upgrade forever.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle