Skip to content
Muhammet Şafak
tr
Web Development 4 min read

Real-time features in Laravel: WebSocket and broadcasting

Adding live updates with Laravel Broadcasting: event-driven publishing, Pusher integration, and listening on the client side.


Implementing real-time updates in an application means stepping outside HTTP’s request-response cycle. Having the user refresh the page to see new data is no longer good enough — when something changes on the server, the client needs to know immediately. Notifications, live counters, transaction status updates: they all share the same underlying requirement.

Laravel addresses this through its Broadcasting layer. The model is event-driven: you fire an event on the PHP side, and that event is delivered to the client over a WebSocket channel. For the transport layer you can use Pusher, Ably, or a self-hosted Soketi instance.

How broadcasting works

The basic flow looks like this:

  1. You create an Event class in PHP and implement the ShouldBroadcast interface.
  2. When you fire the event, Laravel sends a message to the configured broadcast driver.
  3. On the client side (JavaScript) you subscribe to the same channel using the Echo library.
  4. When the message arrives, your listener runs.
// app/Events/OrderStatusUpdated.php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class OrderStatusUpdated implements ShouldBroadcast
{
    public function __construct(
        public readonly int $orderId,
        public readonly string $status
    ) {}

    public function broadcastOn(): Channel
    {
        return new Channel('orders.' . $this->orderId);
    }

    public function broadcastAs(): string
    {
        return 'status.updated';
    }
}

Dispatching the event is identical to any other Laravel event:

broadcast(new OrderStatusUpdated($order->id, 'shipped'));

Listening on the client side

Laravel Echo is the official library for managing WebSocket channels in JavaScript. To work with Pusher you also need the pusher-js package:

npm install --save laravel-echo pusher-js
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

const echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
});

echo.channel('orders.' + orderId)
    .listen('.status.updated', (data) => {
        console.log('Order status:', data.status);
        // Update the UI
    });

Channel types

Laravel offers three channel types:

Public Channel: Any client can subscribe. Suitable for general notifications and live counters.

Private Channel: Authentication is required before subscribing. You use PrivateChannel instead of Channel, and define the authorization rule in routes/channels.php.

// routes/channels.php
Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
    return $user->id === Order::findOrNew($orderId)->user_id;
});

Presence Channel: Builds on top of the private channel and adds awareness of who is currently subscribed. Used for collaborative spaces and “who’s online” type features.

broadcastWith and shaping the payload

You can use the broadcastWith method to customize the data your event carries. By default Laravel sends all public properties of the class, but sometimes that means too much data or you want to include a related model:

public function broadcastWith(): array
{
    return [
        'order_id' => $this->orderId,
        'status'   => $this->status,
        'label'    => __('order.status.' . $this->status),
    ];
}

If you forget to add this method and there is a sensitive field among your public properties (say, a user ID or pricing information), that data can leak to the client. Defining broadcastWith explicitly on every event eliminates that risk.

Reverb: the self-hosted option

If you would rather not depend on a third-party service like Pusher or Ably, you can host Laravel’s own WebSocket server, Reverb. It was officially announced in early 2024; before that, Soketi was the go-to alternative.

For those who want to manage their own infrastructure, the beyondcode/laravel-websockets package is still a valid choice. Because it is compatible with the Pusher protocol, the client-side code remains unchanged.

WebSocket vs. polling: when to use which

WebSocket is not the best solution for every scenario. Infrastructure costs scale with the number of concurrent connections. If “instant” delivery is not critical and updates are infrequent, simple polling often achieves the same result with far less complexity.

The rule of thumb I use in my own projects: if an update only concerns the user who triggered an action and a few seconds of delay is acceptable, polling is sufficient. If multiple users need to see the same data in real time, or if the server is the one initiating the push, WebSocket is the better fit.

Where WebSocket clearly wins is in scenarios that require messages to flow from the server to the client unprompted: instant notifications, live progress bars, multi-user interactions — polling simply does not feel responsive enough for these. The choice should always be proportional to the actual requirement.

The Broadcasting layer removes the unnecessary friction of wiring this infrastructure into a Laravel application. Fire an event, define a channel, listen on the client. Everything beyond that is largely handled by the framework.

Tags: #Laravel
Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

Related Posts

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind