devhereco/livewire-delete-confirmation
devhereco/livewire-delete-confirmation provides a lightweight way to add confirmation prompts before delete actions in Laravel Livewire components. It helps prevent accidental deletions, integrates with existing flows, and keeps destructive operations safe with minimal setup.
v1.0 is a brand-new package, so treat the release notes as a feature summary rather than API documentation — they describe what it does (confirm + delete an Eloquent record from a Livewire component, with success/error messaging) but not the method names or traits. Your first 10 minutes should be spent reading the actual source, not the README.
Start here, in order:
vendor/ source — open the package directory and read the service provider and any trait/abstract class. That's where the real API lives. Look for:
ServiceProvider that registers Livewire component(s) or publishes config/viewsConfirmsDeletion) your components useresources/views — if it exists, you'll likely need to publish it to restyle)composer.json of the package — confirm the required PHP/Laravel/Livewire versions. v1.0 packages frequently pin Livewire 3.x only; you need to know before you try to drop it into a Livewire 2 app.Minimal first use case: a single "Delete" button on an index or detail page.
composer require <vendor>/livewire-confirm-delete
Then, in a component that owns the record:
use Livewire\Component;
// use ThePackage\Concerns\ConfirmsDeletion; // adjust to the real trait name
class ShowPost extends Component
{
public Post $post;
public function delete()
{
$this->post->delete();
session()->flash('success', 'Post deleted.');
return redirect()->route('posts.index');
}
}
and in the Blade view, wire the button to delete — either through the package's dialog component or, if it delegates to Livewire's built-in browser confirm, via wire:confirm. The first thing to verify after install is which of those two it actually is; that single detail determines your entire styling and testing approach.
Treat it as a thin wrapper, not a framework. The value proposition is small: confirmation UX + flash messaging around ->delete(). Keep your own deletion logic (authorization, cascade cleanup, events) in the component method and let the package handle only the prompt and the message display.
Pattern: one deletion method per model, named consistently. If you have a dozen CRUD screens, standardise on a delete() method signature so the package's dialog can be reused without per-model wiring:
public function delete()
{
$this->authorize('delete', $this->post); // keep this — see Gotchas
$this->post->delete();
$this->dispatch('record-deleted'); // let parent/list refresh
return redirect()->route('posts.index');
}
Pattern: list + row-level delete. In an index component holding a collection, prefer deleting by ID rather than binding a model property per row, so a stale model binding can't delete the wrong record after a refresh:
public function delete(int $postId)
{
Post::findOrFail($postId)->delete();
}
If the package's dialog expects a bound model property (common in v1 packages), you'll need a small confirmingId property plus a confirmDelete() / delete() split — the "select then act" pattern. Check whether the package already provides that split before writing it yourself.
Pattern: test the confirmation, not just the deletion. Livewire's testing helpers make this cheap, and confirmation flows are exactly where regressions hide:
Livewire::test(ShowPost::class, ['post' => $post])
->call('delete')
->assertRedirect(route('posts.index'));
$this->assertDatabaseMissing('posts', ['id' => $post->id]);
The assertion that actually catches bugs is the negative one: calling the delete method should not work for a user who lacks permission.
Integration tip: keep flash keys stable. Whatever session key the package's Blade partial renders (success, error, status), reuse it everywhere. If you mix your own session()->flash('message', ...) alongside it, you'll end up with a layout that renders one and silently swallows the other.
Integration tip: check for a published config before customising. If php artisan vendor:publish exposes config or views, publish them and diff against the vendor originals — editing vendor files directly means your changes vanish on the next composer update, which for a 1.0 package could be next week.
No authorization is implied by the package name. Confirm-delete packages handle the UI prompt; they do not add policies. Anyone who can reach the Livewire endpoint can call your delete() method directly — Livewire methods are public HTTP entry points. Always $this->authorize(...) (or Gate::authorize) inside the method, not just hide the button in Blade. Hidden buttons are not access control.
Soft deletes and cascades are invisible to it. The package calls your code; it doesn't know about SoftDeletes, onDelete('cascade') at the DB level, or model events. If a record has dependents, decide explicitly whether you want a soft delete, a hard delete with cascade, or a block-with-error-message. The package's "error message" feature is only useful if you actually throw or catch something — wrap risky deletes in a try/catch and surface the failure through the package's error path.
Flash messages vanish on redirect. If your delete() returns a redirect (very common), confirm the package's message rendering survives it. Session flash does; a component-scoped property does not. If messages aren't appearing, this is almost always the cause — test by calling the method without the redirect.
Version pinning matters more than usual. A 1.0 release has had no deprecation cycle. Pin to ^1.0 and read the changelog before minor bumps, at least until the package has a track record. Check the Livewire major version constraint explicitly — Livewire 2 vs 3 have incompatible confirmation idioms (wire:confirm is a 3.x feature).
Modal/dialog stacking. If the confirmation is a custom modal rather than the browser-native dialog, it renders inside the component's DOM. In nested components or a wire:key-ed list, you can get a dialog bound to the wrong row. Give each rendered row's dialog a unique wire:key, or render a single dialog at the component root driven by a confirmingId property.
Debugging checklist when nothing happens. In this order: (1) is the Livewire script tag present once, not twice? (2) did the service provider register — run php artisan about or check bootstrap/cache; (3) does the method name in wire:click match exactly, including case; (4) are you looking at a cached view — php artisan view:clear. Livewire failures are usually silent in the browser console until you enable the network tab and watch the POST response.
Extension points to check before forking. Look for a publishable config (message text, dialog classes), a publishable view partial, and any events dispatched on delete. If the package dispatches an event, hook your audit logging and cache invalidation there rather than duplicating it in every component. If it has none of these, wrap the package's dialog in your own Blade component on day one — that gives you a single seam to change styling and behaviour without touching call sites later.
Watch the release cadence. With only a 1.0 announcement to go on, there's no evidence yet about maintenance responsiveness. Before adopting this across many models, use it on one screen, and keep the deletion logic in your own methods so swapping the package out is a Blade change rather than a refactor.
How can I help you explore Laravel packages today?