Core
Notifications
Pluggable notification system with multiple drivers.
On this page
- Drivers
- Which driver for what?
- Notification Builder
- Notification API
- Usage in Actions
- Usage in Components
- Usage in Forms
- Configuration
- Driver Resolution Order
- Custom Drivers
- Blade Component
- Toasts
- Countdown bar
- Persistent toasts
- Action buttons
- Stacking & overflow
- Accessibility
- Triggering Toasts from JavaScript
Pluggable notification system with multiple drivers.
Drivers
| Driver | Class | Delivery | Requirements |
|---|---|---|---|
| Current component | CurrentComponentDriver |
Decorator — resolves the active Livewire component via Livewire::current(), then delegates to a wrapped driver (SessionDriver by default) |
None (default) |
| Session | SessionDriver |
session()->flash() + a Livewire event carrying the full payload |
None |
| Livewire | LivewireEventDriver |
Livewire $dispatch() browser event with the full payload |
Frontend listener (toast container) |
| Flasher | FlasherDriver |
PHP Flasher integration | php-flasher/flasher-laravel |
| Null | NullDriver |
No-op — discards everything | None |
The built-in default is CurrentComponentDriver wrapping SessionDriver: it resolves the currently rendering Livewire component itself, so call-sites never have to pass $this. Both SessionDriver and LivewireEventDriver forward the full payload (title, duration, icon, actions, …), so rich toasts survive the server round-trip.
Which driver for what?
| Use this driver when… | Driver |
|---|---|
| You want zero-setup feedback that survives redirects / full page loads (flash), with a basic live toast as a bonus — good default for server-rendered and redirect-after-action flows. | SessionDriver |
Your UI is the toast container and you want rich, instant toasts (title, duration, icon) without a reload. Recommended pairing with <x-wire-notifications::toast-container />. |
LivewireEventDriver |
| Your app already uses php-flasher (Toastr / Notyf / SweetAlert adapters) and you want notifications to flow into that existing UI. | FlasherDriver |
| You want to disable notifications — tests, queued/background jobs, or any context with no user to notify. | NullDriver |
Which drivers feed the toast container?
<x-wire-notifications::toast-container />is an Alpine listener on a Livewire browser event, so only event-dispatching drivers reach it: the defaultCurrentComponentDriver,SessionDriver, andLivewireEventDriver— all forward the fulltitle/duration/icon/actionspayload.FlasherDriverrenders its own UI and bypasses the container;NullDrivershows nothing.
Notification Builder
Notification is an immutable value object. Create via static factory, then send through NotificationManager.
use NyonCode\WireCore\Notifications\Notification;use NyonCode\WireCore\Notifications\NotificationManager; // Shorthand factories — create and send immediatelyNotificationManager::success('User saved');NotificationManager::error('Failed to delete'); // Build a notification, then send$notification = Notification::success('The user was successfully updated.') ->title('Record Saved'); NotificationManager::send($notification); // Full customization$notification = Notification::make('success', 'Changes saved.') ->title('Done') ->icon('check') ->duration(5000) // ms, 0 = persistent ->position('top-right') // top-right, top-left, bottom-right, bottom-left ->extra(['link' => '/details']); NotificationManager::send($notification);
Notification API
// Static factories (return a new Notification instance)Notification::make(string $type, string $message): staticNotification::success(string $message): staticNotification::error(string $message): staticNotification::warning(string $message): staticNotification::info(string $message): static // Fluent immutable modifiers (each returns a new instance)->title(?string $title): static->duration(?int $ms): static // auto-dismiss time, 0 = persistent->persistent(bool $on = true): static // sticky toast: duration 0, no countdown bar->icon(?string $icon): static->position(?string $position): static->extra(array $data): static // arbitrary extra data (merged)->action(NotificationAction|string $action, ?string $event = null): static // append an action button->actions(array $actions): static // replace the action button set->toArray(): array // serialize to array // Sending (via NotificationManager)// $livewire is optional — the default CurrentComponentDriver resolves the// active component itself, so you normally omit it.NotificationManager::send(Notification $n, ?NotificationDriver $driver = null, mixed $livewire = null): voidNotificationManager::success(string $message, ...): voidNotificationManager::error(string $message, ...): voidNotificationManager::warning(string $message, ...): voidNotificationManager::info(string $message, ...): void
Usage in Actions
Action::make('save') ->action(function ($record, Action $action) { $record->save(); $action->sendSuccessNotification(); }) ->successNotification('Saved!'); // Custom notification from action$action->sendNotification( Notification::success('Done') ->title('Processed') ->duration(3000) ->icon('check'));
Usage in Components
use NyonCode\WireCore\Notifications\Concerns\InteractsWithNotifications;use NyonCode\WireCore\Notifications\Notification; class MyComponent extends Component{ use InteractsWithNotifications; public function save(): void { // ... save logic // Type shortcuts (take a message string) $this->notifySuccess('Record saved'); $this->notifyError('Save failed'); $this->notifyWarning('Careful'); $this->notifyInfo('Heads up'); // Or send a fully-built Notification $this->notify( Notification::success('Record saved')->title('Done')->duration(5000) ); }}
Usage in Forms
Forms automatically send a success notification after save() unless disabled:
Form::make() ->schema([...]) ->model(User::class) ->successMessage('User saved!') // custom message ->save(); // DisableForm::make() ->schema([...]) ->disableSuccessNotification() ->save();
Configuration
// config/wire-core.phpreturn [ 'notifications' => [ 'default' => env('WIRE_NOTIFICATIONS_DRIVER', 'session'), // session, livewire, flasher, null ],];
This config value drives the container-bound NotificationDriver (resolved by the service provider for constructor/app() injection).
Driver Resolution Order
When you call NotificationManager::send() (or its shortcuts), the driver is resolved in this order:
- Explicit driver passed to the call / component (
setNotificationDriver(), the$driverargument) - Global default set via
NotificationManager::setDefaultDriver() - Fallback: built-in
CurrentComponentDriverwrappingSessionDriver
Note: the static
NotificationManagerdoes not readwire-core.notifications.defaulton its own — that config only feeds the container binding. To make the configured driver the global default for the static API, bridge it once in a service provider:use NyonCode\WireCore\Notifications\Contracts\NotificationDriver;use NyonCode\WireCore\Notifications\NotificationManager;NotificationManager::setDefaultDriver(app(NotificationDriver::class));
Custom Drivers
Implement the NotificationDriver contract — its single send() method receives the notification and (optionally) the Livewire component in scope:
use NyonCode\WireCore\Notifications\Contracts\NotificationDriver;use NyonCode\WireCore\Notifications\Notification; class SlackDriver implements NotificationDriver{ public function send(Notification $notification, mixed $livewireComponent = null): void { Http::post('https://hooks.slack.com/...', [ 'text' => $notification->title . ': ' . $notification->message, ]); }}
Register it as the global default in a service provider (boot()):
use NyonCode\WireCore\Notifications\NotificationManager; NotificationManager::setDefaultDriver(new SlackDriver());
Or use it for a single component/call without changing the global default:
$this->setNotificationDriver(new SlackDriver()); // per-component (trait)NotificationManager::send($notification, new SlackDriver()); // per-call
Blade Component
Place the toast container in your layout:
<x-wire-notifications::toast-container />
You can customize the position, the fallback auto-dismiss duration, and the browser event it listens for:
<x-wire-notifications::toast-container position="bottom-right" :duration="5000" event-name="table-notification" />
| Prop | Default | Purpose |
|---|---|---|
position |
top-right |
top-left / top-center / top-right / bottom-left / bottom-center / bottom-right |
duration |
4000 |
fallback auto-dismiss (ms) for notifications without their own duration |
event-name |
table-notification |
the window event it listens for (x-on:{eventName}.window) |
progress |
true |
show the per-toast countdown bar (see below) |
stack |
false |
collapse toasts into a pile that fans out on hover |
max |
0 |
cap the number of visible toasts (0 = unlimited); the overflow collapses into a “+N more” pill |
Toasts
Everything below is rendered by <x-wire-notifications::toast-container /> — the drivers only dispatch payloads; the container decides how a toast looks and behaves.
Countdown bar
Each auto-dismissing toast shows a thin countdown bar along its bottom edge that depletes as the toast ages, so users can see how long until it closes. Hovering any toast pauses the bar and the auto-dismiss (and resumes on leave). The bar is on by default and colored by the notification type.
- It is optional — pass
:progress="false"to hide it. - Persistent toasts have no bar — a sticky toast never counts down, so there is nothing to show (see below).
<x-wire-notifications::toast-container :progress="false" /> {{-- no countdown bar --}}
Persistent toasts
Call ->persistent() (or ->duration(0)) to make a toast sticky: it stays until the user dismisses it and shows no countdown bar. Ideal for messages that require a decision.
NotificationManager::send( Notification::warning('Payment needs review before it can settle.') ->title('Action required') ->persistent());
Action buttons
Add buttons that dispatch a Livewire event on click — the "Undo" affordance. Your host component listens with #[On(...)].
use NyonCode\WireCore\Notifications\Notification;use NyonCode\WireCore\Notifications\NotificationAction; // shorthand: label + Livewire eventNotificationManager::send( Notification::success('Item deleted')->action('Undo', 'restore-record')); // full controlNotificationManager::send( Notification::success('Order #1042 saved')->action( NotificationAction::make('Undo', 'restore-record') ->payload(['id' => 1042]) // sent with the dispatched event ->color('primary') // button accent (falls back to the toast type) ->keepOpen() // don't dismiss the toast after clicking ));
// in the host Livewire component#[On('restore-record')]public function restore(int $id): void{ // …}
NotificationAction is an immutable value object: make(label, event), ->payload([...]), ->color(...), ->keepOpen(). Clicking dispatches Livewire.dispatch(event, payload) and (unless keepOpen()) closes the toast.
Stacking & overflow
stackcollapses toasts into a tidy pile; hovering the pile fans them out into the full list. The newest toast sits closest to the anchor edge.maxcaps how many are visible at once; extras collapse into a clickable “+N more” pill that reveals the rest.
<x-wire-notifications::toast-container stack :max="5" />
Accessibility
The container is an aria-live="polite" region (error toasts use role="alert"), so screen readers announce toasts as they arrive. It also honors prefers-reduced-motion: when reduced motion is requested, the stack never collapses/fans out and card transitions are disabled.
Triggering Toasts from JavaScript
The toast container installs a global window.wireToast helper (and an Alpine $toast magic) when it mounts, so you can pop a toast straight from the frontend — no server round-trip. The helper simply dispatches the container's eventName window event with the standard payload (type, message, title, duration).
// shorthand — type + messagewireToast.success('Saved');wireToast.error('Something went wrong');wireToast.warning('Careful');wireToast.info('Heads up'); // with options (title, duration, …)wireToast.success('Saved', { title: 'Done', duration: 6000 }); // full payload object (type defaults to 'info' if omitted)wireToast({ type: 'success', message: 'Saved', title: 'Done' });wireToast('Plain info toast');
Inside Alpine, use the $toast magic:
<button @click="$toast.success('Copied!')">Copy</button>
The helper targets the container's configured eventName, so a custom event-name="my-toast" is wired up automatically. window.wireToast is installed once (the first container wins); if you render multiple containers with different event names, dispatch the CustomEvent yourself for the secondary ones:
window.dispatchEvent(new CustomEvent('my-toast', { detail: { type: 'success', message: 'Saved' },}));