Skip to content

Notifications

The notifications bell is an opt-in module: a fluent Notification builder that sends a self-describing payload through Laravel’s native notification channels, and a Notifications bell component that lists, reads, and dismisses them. Nothing is enabled until you publish the migration and mark your notifiable model.

Notifications::make();

The live preview above can’t reach a real endpoint from a static docs page, so it renders empty — in your app it hydrates from the config-driven endpoint on mount.

Notification::make() builds the payload fluently, then ->send() or ->sendToDatabase() dispatches it to any notifiable:

use Lattice\Lattice\Core\Enums\Variant;
use Lattice\Lattice\Notifications\Notification;
Notification::make()
->title('Order #1234 shipped')
->body('Tracking is now available.')
->icon('truck')
->variant(Variant::Success)
->href('/orders/1234')
->action(MarkOrderSeenAction::class, ['order_id' => 1234])
->link('Track shipment', '/orders/1234/track')
->send($order->user);
  • ->title() / ->body() — the headline and supporting text.
  • ->icon($name) — a sprite icon name or a backed enum case.
  • ->variant(Variant::…) — the same Variant enum toasts and callouts use; drives the bell item’s accent color.
  • ->href($url) — makes the whole row a link: clicking the title/body navigates to $url (an internal app route, followed as an Inertia visit) and marks the notification read.
  • ->action($actionClass, $arguments = [], $label = null) — attaches a real Lattice action button, referenced by class name.
  • ->link($label, $url) — attaches a plain link button (an internal app route) instead of (or alongside) an action.
  • ->send($notifiable) — delivers over database and broadcast.
  • ->sendToDatabase($notifiable) — persists only, without a broadcast event.

For a notification that also needs other channels — mail, Slack, SMS — don’t reach for a Lattice base class. Write a plain Laravel notification and return the Notification builder’s payload from toArray(), the same shape ->send() produces:

use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification as LaravelNotification;
use Lattice\Lattice\Core\Enums\Variant;
use Lattice\Lattice\Notifications\Notification;
class OrderShipped extends LaravelNotification
{
public function __construct(private readonly Order $order) {}
public function via(object $notifiable): array
{
return ['mail', 'database', 'broadcast'];
}
public function toArray(object $notifiable): array
{
return Notification::make()
->title("Order #{$this->order->number} shipped")
->body('Tracking is now available.')
->variant(Variant::Success)
->href("/orders/{$this->order->id}")
->toArray();
}
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->line("Order #{$this->order->number} has shipped.");
}
}
$order->user->notify(new OrderShipped($order));

Laravel uses the array returned by toArray() for both the database and broadcast channels, so the bell renders it and pushes it live exactly like ->send() does — no Lattice base class involved, just the builder’s own toArray() as the reusable payload getter.

Drop Notifications::make() into a Topbar like any other component:

use Lattice\Lattice\Core\Components\Stack;
use Lattice\Lattice\Core\Enums\Side;
use Lattice\Lattice\Layouts\Components\Topbar;
use Lattice\Lattice\Notifications\Components\Notifications;
Topbar::make('app-topbar')->sticky()->items([
Stack::make()->direction('row')->float(Side::End)->schema([
Notifications::make(),
]),
]);

It defaults to a popover anchored under the bell. Call ->slideOut() for a full-height panel that slides in from the trailing edge instead — a better fit for a dense topbar or a mobile layout:

Notifications::make()->slideOut();
'notifications' => [
'endpoint' => 'lattice/notifications',
'middleware' => ['web', 'auth'],
'per_page' => 15,
'polling_interval' => null,
'prune_after_days' => 30,
],
  • endpoint / middleware — where the list and mutation routes are served, same convention as every other Lattice endpoint.
  • per_page — page size for the list and its “load more” pagination.
  • polling_interval — seconds between polling refetches; null disables polling (the default — rely on realtime, or set an explicit interval as a fallback). Override per bell with ->pollingInterval($seconds).
  • prune_after_days — how long a read notification is kept before pruning deletes it.

Each notifiable gets a private channel — NotificationChannel::for($notifiable), the same channel Laravel’s broadcast notifications use (or receivesBroadcastNotificationsOn() if the model defines it). The bell subscribes to it with useEchoNotification from @laravel/echo-react and prepends whatever arrives, no page reload needed.

See Realtime for the broadcasting setup (Echo, Reverb) this depends on — this is a different mechanism from a page’s declarative Listen listeners, purpose-built for one notifiable’s private notification stream.

Read notifications older than prune_after_days are deleted by an Artisan command, not automatically:

Terminal window
php artisan lattice:notifications:prune

Schedule it in your application:

use Illuminate\Support\Facades\Schedule;
Schedule::command('lattice:notifications:prune')->daily();

Unread notifications are never pruned, regardless of age.