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 Action Buttons Laravel Package

pavloniym/nova-action-buttons

Adds configurable action buttons to Laravel Nova resources, letting you trigger custom actions, links, or dialog workflows directly from index and detail views. Buttons are defined with simple PHP/JSON configuration and support flexible styling and visibility rules.

View on GitHub
Deep Wiki
Context7

Getting Started

Preconditions, check these before composer require. This is a Nova 5 field. Nova 4 and earlier will install fine and then blow up at render time with class-not-found / method-signature errors, which is a miserable debugging mode on a Nova page. Confirm three things first:

composer show laravel/nova | grep versions     # must be 5.x
php -v                                         # 8.1+ realistically
composer show laravel/framework | grep versions

Then cross-check the package's own composer.json constraints against the exact Nova patch you have pinned in composer.lock, not against "Nova 5" generically. The package is a passenger on Nova's constraints; a Nova 5.x point release is the most likely thing to break it later.

Install.

composer require pavloniym/nova-action-buttons:^2.0

No service provider registration (package auto-discovery), no migrations, no config to publish as a hard requirement. Pin ^2.0 and never upgrade this package independently of Nova.

Where to look first, in this order:

  1. vendor/pavloniym/nova-action-buttons/README.md — the actual API surface. Small packages document by example; copy the example verbatim before improvising.
  2. vendor/pavloniym/nova-action-buttons/src/ — it's a handful of files. Read the field class to see what make() accepts and what the actions() setter is called. Confirm the exact class name/namespace before you paste anything, because this guide uses the conventional shape and small packages do rename between majors.
  3. The v2.0.0 release notes on GitHub, not v2.0.1. v2.0.1 is byte-identical to v2.0.0 — a re-cut of main history. Every real Nova 5 change, every renamed API, every PHP floor lives in the 2.0.0 notes. If the roadmap says "adopt 2.0.1 features," it's wrong; there are none.
  4. vendor/pavloniym/nova-action-buttons/resources/ — check whether it ships publishable views. If it does, that's your override/upgrade story and you need to know now.

First use case — pick a deliberately boring one. One internal, non-revenue-critical resource, one action that already works from the Nova dropdown, index view only. Content-moderation approve/reject or an ops "retry sync" are ideal: the action lifecycle is well understood, the blast radius is small, and you can measure time-to-action before and after. Do not pilot on refunds.

The shape is a field on the resource, holding existing Action instances:

use Pavloniym\NovaActionButtons\ActionButtons; // verify namespace in src/
use App\Nova\Actions\ApprovePost;

public function fields(NovaRequest $request): array
{
    return [
        // ... normal fields
        ActionButtons::make('Actions')
            ->actions([
                ApprovePost::make(),
            ])
            ->onlyOnIndex(),
    ];
}

Note what did not happen: no new controller, no new Vue, no change to ApprovePost. That's the whole value proposition — you're changing where actions are declared, not what they do.

Implementation Patterns

Reuse the action class; never fork it for the button. The button and the dropdown should run the identical handle(). The moment you copy an action to tweak it for buttons, you've created two code paths that will drift and two places to fix a bug.

Keep authorization in the action class. canSee(), canRun(), and Nova policies are resolved by the action, not by the field. Resist the urge to conditionally build the actions([...]) array per user in fields() — that pushes policy logic into the view layer, duplicates the gate, and makes the "why can't I see this button" question require reading two files instead of one.

class ApprovePost extends Action
{
    public function handle(ActionsRequest $request, $models) { /* ... */ }

    public function canSee(NovaRequest $request) { /* policy here */ }
    public function canRun(NovaRequest $request) { /* policy here */ }

    public function confirmText() { return 'Approve this post?'; }  // destructive → always set this
}

Confirmation is a property of the action, not the button. Set confirmText() on the action and both surfaces get the modal. A visible button is a much larger click target than a dropdown item, so confirmation on anything destructive is not optional — and double-submit safety belongs in handle(), not in the UI, because an idempotent handle() protects the dropdown users too. firstOrCreate, a state-machine guard (if ($order->status !== 'pending') return;), or a queued action with a unique key all work. Pick one and be explicit about it.

Scope visibility declaratively. onlyOnIndex() / onlyOnDetail() on the field for surface scoping; the action's own onlyOnIndex / onlyOnDetail still applies underneath. Use the field-level one to decide where the button appears, the action-level one to keep the action consistent across both surfaces. Test lenses and relationship index pages explicitly — those are the contexts where Nova field packages most often resolve differently than you'd expect.

Split row-scoped from global. Row-scoped lifecycle actions (approve, retry, escalate) go in fields() so they render per row. Global actions (bulk export, reindex) should stay in the resource toolbar / actions() method where Nova's select-rows model already works. Don't recreate bulk selection with a per-row button.

Don't try to force form-bearing actions into buttons. If an action declares fields() and expects the user to fill in a reason, amount, or note, a one-click button that skips straight to handle() is the wrong UX and possibly a data-integrity problem. Check what the package does when the action has fields before you ship it; if it bypasses the form, those actions stay in the dropdown.

Rollout cadence — one resource at a time. Pilot → staging verification → two weeks → measure → expand. Verify in staging, not production: authorization parity (log in as a few real roles and confirm canSee/canRun produce the same visible button set as the dropdown did), confirmation modals firing, lens and relationship-index rendering, index P95 render time before/after, and narrow-viewport layout. The four numbers that justify or kill expansion: time-to-action for the target workflow, support tickets absorbed by the button, admin error rate, P95 index render.

Gotchas and Tips

v2.0.1 is a no-op — don't let the patch bump fool you. No code changes, no fixes. If v2.0.0 had a defect, v2.0.1 still has it. Any bugfix signal you think you're reading is housekeeping from a cleaned-up main history.

Version mismatch fails at render time, not install time. Composer will happily resolve this against Nova 4 and you'll get a white page or a class-not-found in the Nova JS console. In a multi-app portfolio with mixed Nova majors, that's an upgrade trap — check the Nova major before you touch composer.json.

Per-row authorization cost is the real performance question. Each button is an action Nova authorizes per row. Six buttons on a 50-row index is roughly 300 canSee evaluations per page load. Fine with normal policies; catastrophic if canSee hits the database or an external API. Grep your target actions' canSee and canRun for queries before you put six of them on a table. If they do I/O, memoize per request.

Double-submit is a UI change, not a backend one. A button is a bigger target than a dropdown item, and admins click it twice. The package can't fix this; handle() must. Audit every action you surface for idempotency before the pilot, not after the first duplicate refund.

Long button labels break table layout. Two-word labels and icons are fine; "Resend the invoice to the customer's billing email" will wrap and displace the row. Truncate in name() on the button if needed, keep the long description in the action's tooltip/confirmation.

If it ships publishable views, publish deliberately and diff after every upgrade. Blade overrides are the classic silent-regression source in Nova packages: the package updates, your stale override keeps rendering old markup against a new API, and nobody notices until an operator reports a broken button. diff your overrides against vendor/ after every composer update that touches this package.

Name a fork owner before you make this load-bearing. ~15 stars, zero dependents, single maintainer, MIT. That's fine for internal tooling and genuinely risky as a load-bearing dependency for revenue-critical back-office flows. Cheapest insurance available: identify an engineer who can fork and patch, record them as risk owner, and keep the fork option open. Zero dependents also means essentially no external production validation — you are an early adopter, and when a Nova 5.x point release breaks the field you'll be debugging it first, alone.

Debug the field as a field. If buttons don't appear, the problem is almost always resolution, not the package: check the resource's fields() is actually hit on the surface you're on (index vs detail use different resolution paths in Nova), check the field isn't hidden by a canSee on the field itself, and check the action isn't filtered out by onlyOnIndex/onlyOnDetail at the action level. Add a ->help() or ->withMeta() temporarily to confirm the field is rendering at all before assuming the action wiring is broken.

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