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

dnwjn/nova-button

A Laravel Nova package for adding customizable buttons to resource pages, letting you trigger links, actions, or modals directly from Nova while controlling visibility and authorization.

View on GitHub
Deep Wiki
Context7

Getting Started

Install pinned to your Nova major — this is non-negotiable. The versioning story for this package is one sentence long: 5.x = Nova 5, 4.x = Nova 4 (frozen), and release 5.0.0 dropped v4 outright in the same commit that added v5 support. Pin the exact constraint and record the pairing in your repo (README or a NOVA_COMPAT note):

composer require dnwjn/nova-button:^5.0   # Nova 5 only
php artisan nova:publish
php artisan optimize:clear

nova:publish is load-bearing if the package ships uncompiled Vue or expects Nova's asset pipeline to pick up its component — confirm this by checking whether dist/ is committed upstream. A blank or unstyled button after install is almost always a stale asset bundle, not a code bug.

First read, in order:

  1. The README compatibility table. It is the only authoritative statement of which package major pairs with which Nova major, plus PHP/Laravel constraints.
  2. The Button field class itself. It's small. You are looking for the fluent surface (make, action, url, route, icon, confirm, canSee, style, target) and — far more important — what the click actually hits. Nova's action endpoint and middleware, or a bespoke route the package registered. That single fact determines your whole security posture and whether you can ship this against anything that touches records.
  3. The Vue component. Note how the click dispatches and whether it surfaces Nova's action progress/feedback UI, because a button that silently hangs for ten seconds reads as broken to operators.

First use case — smoke test on the cheapest surface. Add one non-destructive button to a detail view that dispatches a Nova Action you already have policies on. "Resend invite" is ideal: idempotent-ish, low blast radius, and it validates provider registration, asset loading, field placement, and authorization in one shot.

use Dnwjn\NovaButton\Button;

public function fields(NovaRequest $request)
{
    return [
        // ...normal fields

        Button::make('Resend Invite')
            ->action(\App\Nova\Actions\ResendInvite::class)
            ->icon('refresh')
            ->canSee(fn () => $this->resource->isPending()),
    ];
}

Verify every chained method against source before you trust it. At 11 stars the README will not cover the full surface, and method names rarely match what you'd guess from other Nova field packages — a wrong guess here throws at fields() time, so it's cheap to discover, but only if you actually run the screen.

Implementation Patterns

Button as trigger surface, Action as logic. This is the highest-leverage pattern and the one that keeps the package replaceable. Keep business logic in Nova Actions you already own — policies, queued execution, audit hooks, handle() — and use the button purely to surface a labeled, visible entry point where Nova's action dropdown buries it. Before building anything, ask whether the action is already showOnDetail/showOnIndex; if it is, you're adding a nicer affordance, not a capability. Be honest about that.

Route-linked buttons for work that isn't an action. Exports, PDF downloads, external CRM jumps, "open in Stripe." Point the button at a URL or route and put the target behind a policy-protected controller. The button is UI; it is never a security boundary.

Centralize button factories. Buttons are fields, so they compose with the same patterns as fields. Factor them into a trait or base resource class so a package major bump is one change-site instead of twelve:

trait HasApprovalButton
{
    protected function approvalButton(): Button
    {
        return Button::make('Approve')
            ->action(ApproveVendor::class)
            ->confirm('Approve this vendor?')
            ->icon('check')
            ->canSee(fn () => $this->resource->isPending());
    }
}

Two-layer defense: canSee for UX, policy for truth. canSee closures hide buttons on records the operator can't act on — good hygiene, prevents misclicks on records they can't touch. But separately confirm the dispatched Action (or controller) enforces the same policy server-side. Nova's native Action dispatch does; the open question is whether this package's click path does, if it invented its own route. Read that before you assume.

Group related controls into a decision toolbar. Approve / Reject / Hold in a row makes the decision set visible at a glance — a genuine ergonomic win over hunting a dropdown, and the main reason this package earns its place over native Actions. Order them by consequence: safest first, destructive last, destructive one gets .confirm().

Confirm every mutation. Destructive or financially material actions get a confirmation dialog, and the dialog is not the only safeguard — pair it with an idempotent handle() and a policy. If an action isn't confirmed and isn't idempotent, it's a foot-gun regardless of how it's triggered.

Show feedback for anything slow. If the action queues or takes more than a second or two, wire it into Nova's action feedback rather than letting the click go silent. Check the Vue component for whether it participates in that UI at all; if it doesn't, prefer the native action for long-running work and use the button only for fast, synchronous operations.

Compose with dependsOn/onlyOnDetail like any field. Buttons can sit next to status fields, mirror their visibility conditions, and appear only where the decision is actually made.

Gotchas and Tips

Version drift is the #1 operational risk. Nova 4→5 and package 4.x→5.x is one change, one PR, one rollback point — they cannot be validated independently. Any roadmap item called "upgrade Nova" silently contains this package's upgrade too; put it in that line item, not its own. Never float the constraint.

Nova 4 is a dead branch. 5.0.0 dropped v4 with no shim and no deprecation runway. If you're on v4, you're pinned to a frozen 4.x line — a worse position than vendoring. The surface is one small field class; copying it into app/Nova/ is a realistic afternoon.

Authorization is the open question — answer it from source, not from the major bump. A Nova-compatibility major bump implies nothing about whether the dispatch endpoint is hardened. Confirm the click goes through Nova's action endpoint, Nova middleware, and Nova policies. If it invents its own route, you own the policy checks before it touches a record. This matters more than any UI concern and should be the first thing you verify before shipping against real data.

Assets after every Nova or package upgrade. php artisan nova:publish then clear stale build output. Blank, unstyled, or unclickable buttons are almost always a stale bundle.

Debugging "the button doesn't appear" / "nothing happens":

  • Is the field actually in fields() for that view? Some packages require explicit .showOnIndex() / .showOnDetail().
  • Is canSee returning false? Temporarily test with fn () => true.
  • Did you run nova:publish since install/upgrade? Check the console for a 404 on the JS asset.
  • Is the action class correctly namespaced and autoloadable? A typo'd FQCN surfaces as a blank or broken component, not always a clear error.
  • On click: watch the network tab, and add a Log::info() at the top of handle() to confirm it actually ran. Silence usually means the request went somewhere else.

Watch field-ordering and layout. Buttons render inline in the field stack and may not respect your mental model of the grid. If a button lands in a weird column, wrap it or move it to the end of the field array rather than fighting the component.

Extension points are your safety valve. Subclass the Button class or override the Vue component for anything the fluent API doesn't cover — custom styling, a different confirm dialog, a status badge baked into the label. Once you need more than one override, vendor the whole component; partial dependency with partial override is the worst of both worlds.

Keep critical logic out of the button. Validation, permission matrices, audit logging, multi-step confirmations all belong in the Action or a controller where they're testable and survive a package swap. The button is a click target and a label — anything more is mis-scoped and should be re-specced as application logic.

Low adoption is a real constraint, not a formality. Zero dependents means nobody has absorbed the next Nova major ahead of you. Budget one manual smoke test per Nova release: render an index, render a detail page, click a button, confirm the action fired and authorization held. That's your fastest early warning and it costs ten minutes.

When to just vendor it. If the feature matters and you can't afford drift — or you're on Nova 4 — copy the field into app/Nova/ and own it. It's a small MIT-licensed component; the honest framing is "depend vs. copy," not "buy vs. build." For a one-day internal admin affordance, copying is frequently the right call.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource