Real-time Laravel with Reverb closed the last major gap in the framework\'s first-party ecosystem. For years, live features meant paying for Pusher, running a community WebSocket server, or bolting on Socket.IO with Redis glue. Laravel Reverb โ introduced alongside Laravel 11 โ is an official, blazing-fast WebSocket server that speaks the Pusher protocol, which means the entire existing broadcasting stack (events, channels, Echo) works with it unchanged. This post walks through the moving parts and the patterns for building presence lists, live notifications, and updating UIs.
The broadcasting pipeline in one paragraph
Something happens on the server โ an order ships, a comment lands, a job finishes. You fire a Laravel event implementing ShouldBroadcast. Laravel serializes it, pushes the broadcast through a queue worker, and hands it to Reverb, which delivers it over open WebSocket connections to every subscribed client. In the browser, Laravel Echo maintains the connection and invokes your listener. The result: server-side PHP causes browser-side updates in tens of milliseconds, with no polling.

Getting the stack running is one installer command โ php artisan install:broadcasting โ which installs Reverb, publishes the config, sets the environment keys, and scaffolds Echo on the frontend. Locally, php artisan reverb:start gives you a WebSocket server on port 8080.
Broadcasting an event
class OrderShipped implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public Order $order) {}
public function broadcastOn(): array
{
return [new PrivateChannel('orders.'.$this->order->user_id)];
}
public function broadcastWith(): array
{
return [
'order_id' => $this->order->id,
'status' => 'shipped',
'eta' => $this->order->eta?->toIso8601String(),
];
}
}
Two details deserve attention. First, define broadcastWith() explicitly โ without it, all public properties are serialized into the payload, which is how internal fields end up pushed to browsers. Treat broadcast payloads with the same curation as API resources. Second, broadcasting goes through your queue by default; implement ShouldBroadcastNow only for the rare event where queue latency is unacceptable, because synchronous broadcasting adds the WebSocket round-trip to the originating request.
Channels: public, private, and presence
Channel choice is your authorization model. Public channels require no auth and suit genuinely public data โ a live scoreboard, a status page. Private channels require the client to pass an authorization callback defined in routes/channels.php:
Broadcast::channel('orders.{userId}', function (User $user, int $userId) {
return $user->id === $userId;
});
Presence channels are private channels that additionally share member information with everyone subscribed โ the primitive behind "who is online" lists, collaborative-editing avatars, and typing indicators. The callback returns user data instead of a boolean:
Broadcast::channel('document.{id}', function (User $user, int $id) {
return $user->can('view', Document::find($id))
? ['id' => $user->id, 'name' => $user->name]
: false;
});
On the client, Echo\'s presence API gives you the roster and membership events:
Echo.join(`document.${documentId}`)
.here((users) => renderAvatars(users))
.joining((user) => addAvatar(user))
.leaving((user) => removeAvatar(user))
.listen('DocumentUpdated', (e) => applyPatch(e.patch));
For lightweight ephemeral signals like typing indicators, use whisper events โ client-to-client messages that never touch your PHP application: channel.whisper('typing', {...}) on one side, listenForWhisper('typing', ...) on the other. No server round-trip, no events to define.
Live notifications with almost no code
Laravel\'s notification system broadcasts natively. Add broadcast to a notification\'s via() array, and every notifiable user gets a private channel; Echo has a dedicated helper:

Echo.private(`App.Models.User.${userId}`)
.notification((n) => toast(n.title));
Pair this with database notifications and you get the standard product pattern โ a persistent notification inbox that also pops toasts in real time โ with a few dozen lines total. Livewire users get an even shorter path for many cases: the #[On('echo:orders.{order.id},OrderShipped')] attribute lets a component re-render when a broadcast arrives, no hand-written JavaScript at all.
Running Reverb in production
Reverb is a long-running PHP process built on ReactPHP, and it is legitimately fast โ a single modest server holds tens of thousands of concurrent connections. Production checklist:
- Supervise the process (Supervisor or systemd) and raise OS limits โ each connection consumes a file descriptor, so
ulimitand event-loop settings matter at scale. Forge has a first-class Reverb integration that handles this. - Terminate TLS at Nginx and proxy
wss://upgrade requests to Reverb\'s port; browsers on HTTPS pages require secure WebSockets. - Remember the queue. Broadcasts ride the queue, so a stalled worker looks exactly like "Reverb is broken." When real-time features misbehave, check Horizon first.
- Scale horizontally when needed by enabling Reverb\'s Redis pub/sub scaling mode, which fans events out across multiple Reverb servers behind a load balancer.
- Restart on deploy with
reverb:restart, for the same stale-code reasons as queue workers.
Because Reverb speaks the Pusher protocol, migration paths stay open in both directions: an app on Pusher or Ably moves to Reverb by swapping environment credentials, and back again if you would rather pay than operate. That protocol compatibility, more than any single feature, is what makes adopting it low-risk.
Design habits for live UIs
Broadcast facts, not render instructions โ OrderShipped with an ID and status, letting each client decide how to react, ages better than pushing HTML fragments. Keep payloads small and let clients fetch details over HTTP if they need more; the socket is for signaling. And always build the non-realtime fallback first: a page that loads correct data on refresh, then layers live updates on top, degrades gracefully when a corporate proxy eats the WebSocket. Real-time should be an enhancement, not a dependency.

Closing thoughts
The pieces โ Reverb, broadcasting events, channel authorization, Echo, and Livewire\'s listeners โ compose into live features with remarkably little code: presence lists in an afternoon, live notifications in an hour. What changed with Reverb is not the API, which Laravel has had for years, but the ownership model: real-time is now a first-party, self-hosted, protocol-compatible part of the framework rather than a monthly bill or a community gamble. If you have been deferring live features as "later" complexity, later has arrived โ and it is one installer command away.
Related Service
๐ป Web Development
Custom websites and web applications built with PHP, Laravel, WordPress, and React by a Chandigarh & Mohali based web developer โ fast, secure, scalable, and trusted by clients across India and worldwide.
Explore Web Development →
Reviews & Comments
Reviews are moderated and appear after approval.
No reviews yet โ be the first to share your thoughts.
Leave a Review