Features
Events
Events let components tell each other that something happened. A component dispatches an event, from the browser or from the server, and every component listening with a matching #[On] runs its method.
On this page
Use events for news, not for keeping state. An event might carry a message to show, a list to refresh or a
sound to play. A component dispatches an event with $this->dispatch(), or with
$dispatch() from its view. Components that listen with #[On] then run their
method, with the event's parameters. If several components need to read the same state, use a
store instead.
No messages yet.
The first button dispatches notify from its view, in the browser. The second button calls a
#[Server] method that dispatches the event on the server. That event arrives with the
server's answer and reaches the same listener.
The components
<?php
namespace App\Nitro\Components;
use Nitro\Attributes\Server;
use Nitro\Component;
class Notifier extends Component
{
#[Server]
public function save(): void
{
// A demo: nothing is saved. A real method would write to the database first.
$this->dispatch('notify', 'Saved on the server at '.now()->format('H:i:s'), 'success');
}
}
<div class="flex flex-wrap gap-2 text-sm">
<button type="button" nitro:click="$dispatch('notify', 'Copied to the clipboard')" class="rounded-lg px-3 py-2 font-medium ring-1 ring-slate-300 dark:ring-white/15">Say it from the view</button>
<button type="button" nitro:click="save" nitro:loading.attr="disabled" class="rounded-lg bg-slate-900 px-3 py-2 font-semibold text-white disabled:opacity-50 dark:bg-white dark:text-slate-900">Save, then say it from the server</button>
</div>
Compiling…
<?php
namespace App\Nitro\Components;
use Nitro\Attributes\On;
use Nitro\Component;
class Toaster extends Component
{
public array $toasts = [];
public int $next = 1;
/** Runs when any component dispatches notify, for example with $this->dispatch('notify', 'Saved'). */
#[On('notify')]
public function notify(string $message, string $tone = 'info'): void
{
$this->toasts[] = ['id' => $this->next++, 'message' => $message, 'tone' => $tone];
$this->toasts = array_slice($this->toasts, -3);
}
public function dismiss(int $id): void
{
$this->toasts = array_values(array_filter($this->toasts, fn ($toast) => $toast['id'] !== $id));
}
}
<div class="w-full max-w-sm space-y-2 text-sm">
@forelse ($toasts as $toast)
<div nitro:key="{{ $toast['id'] }}" nitro:transition.duration.200ms @class([
'flex items-center justify-between gap-3 rounded-lg px-3 py-2 ring-1',
'bg-emerald-50 text-emerald-800 ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-500/20' => $toast['tone'] === 'success',
'bg-white text-slate-700 ring-slate-200 dark:bg-slate-900 dark:text-slate-300 dark:ring-white/10' => $toast['tone'] !== 'success',
])>
<span>{{ $toast['message'] }}</span>
<button type="button" nitro:click="dismiss($toast['id'])" class="opacity-60 hover:opacity-100" aria-label="Dismiss">×</button>
</div>
@empty
<p class="text-slate-400">No messages yet.</p>
@endforelse
</div>
Compiling…
Dispatching and listening for events
$this->dispatch('notify', 'Saved'); // Every listener.
$this->dispatch('notify', 'Saved')->to(Toaster::class); // Only Toaster components.
$this->dispatch('refresh')->self(); // Only this component.
#[On('notify')]
public function notify(string $message, string $tone = 'info'): void { ... }
- Every listener on the page hears an event by default. Use
->to(Toaster::class)to reach only the components of that class, or->self()to reach only the component that dispatches it. - Parameters are passed to the listener in order, as arguments. You can name them for readability,
dispatch('post-saved', id: $post->id), but the names don't match them to the listener's parameters: the order does. - A listener runs in the browser, or on the server if it is a
#[Server]method. - Events dispatched on the server by a
#[Server]method arrive with its answer, and are dispatched in the browser at that point.
Dispatching from a view
<button nitro:click="$dispatch('notify', 'Copied')">Copy</button>
<button nitro:click="$dispatchTo('toaster', 'notify', 'Copied')">Copy</button>
<button nitro:click="$dispatchSelf('refresh')">Refresh</button>
<button nitro:click="$parent->save()">Save the form around it</button>
$parent->save() calls a method of the component whose view placed this one. This lets a child
ask its parent to act, without an event.
Listening on the window
An event that you don't narrow with ->to() or ->self() is also a DOM event on the window, so your own scripts can listen for it:
window.addEventListener('notify', (e) => console.log(e.detail.params)); // ['Saved', 'success']
Listening to broadcasts
#[On('echo:...')] listens to Laravel broadcasting through your application's Laravel Echo
(window.Echo, with Reverb or Pusher). The component subscribes when it appears, and it stops
listening when it leaves the page.
#[On('echo:orders,OrderPlaced')] // A public channel.
public function orderPlaced(array $event): void { $this->orders[] = $event['order']; }
#[On('echo-private:orders.{order.id},OrderShipped')] // A private channel. {order.id} reads the state.
#[Server]
public function orderShipped(array $event): void { $this->order = Order::find($this->order['id'])->toArray(); }
#[On('echo-presence:chat,here')] // A presence channel: here, joining, leaving.
public function here(array $users): void { $this->online = $users; }
#[On('echo-notification:App.Models.User.{userId}')] // Notifications.
public function notified(array $notification): void { $this->unread++; }
- A
{property}in a channel name reads the component's state. It must name a public property, and the build tells you if it doesn't. - For an event with a custom name (set with
broadcastAs()), write the name with a leading dot, as inecho:orders,.order.placed. - Laravel authorizes private and presence channels, just as it does for any Echo listener.