Skip to main content

Documentation Index

Fetch the complete documentation index at: https://kawax.biz/llms.txt

Use this file to discover all available pages before exploring further.

How broadcasting works

Broadcasting lets your server notify the browser the moment something happens — an order ships, a message arrives, a file finishes processing — without the browser polling for updates. The flow is:
  1. Something happens on the server (a model updates, a job completes).
  2. Your code fires a Laravel event that implements ShouldBroadcast.
  3. Laravel queues a broadcast job and sends the event payload to a WebSocket server.
  4. The browser, connected to the same WebSocket server via Laravel Echo, receives the event and updates the UI.
Broadcasting is built on top of Laravel’s event system. Events and listeners are the foundation — broadcasting adds a delivery channel. Make sure you’re comfortable with events and listeners before continuing.

Setup

Broadcasting is disabled in new Laravel apps. Enable it with:
php artisan install:broadcasting
This creates config/broadcasting.php and routes/channels.php, and prompts you to choose a driver.
Before any events can be broadcast, you need a running queue worker. Broadcasting dispatches jobs asynchronously to avoid slowing down your HTTP responses.
php artisan queue:work

Laravel Reverb

Reverb is Laravel’s official self-hosted WebSocket server, introduced in Laravel 11. It’s the recommended driver for most applications — no external service account required.

Install Reverb

The quickest path installs everything at once:
php artisan install:broadcasting --reverb
Or install manually:
composer require laravel/reverb
php artisan reverb:install

Environment variables

BROADCAST_CONNECTION=reverb

REVERB_APP_ID=my-app
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

Start the Reverb server

php artisan reverb:start
In production, manage this process with Supervisor or a similar daemon manager.

Channel types

ChannelClassAccess
PublicChannelAnyone can subscribe, no authentication
PrivatePrivateChannelAuthenticated users only; requires an authorization callback
PresencePresenceChannelLike private, but exposes who else is in the channel

Creating a broadcast event

php artisan make:event OrderStatusUpdated
Implement ShouldBroadcast on the generated class:
<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class OrderStatusUpdated implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public function __construct(
        public Order $order,
    ) {}

    public function broadcastOn(): Channel
    {
        return new PrivateChannel('orders.' . $this->order->id);
    }
}
By default, all public properties are included in the broadcast payload. Limit the data with broadcastWith():
public function broadcastWith(): array
{
    return [
        'order_id' => $this->order->id,
        'status'   => $this->order->status,
        'updated'  => $this->order->updated_at->toIso8601String(),
    ];
}
Override the event name clients listen for with broadcastAs():
public function broadcastAs(): string
{
    return 'order.status.updated';
}
When using a custom name, prefix it with . in JavaScript to bypass Echo’s default namespace:
Echo.private(`orders.${orderId}`)
    .listen('.order.status.updated', (e) => {
        console.log(e);
    });

Authorizing private channels

Private and presence channels require a server-side authorization check before a client can subscribe. Define authorization callbacks in routes/channels.php:
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
    return $user->id === Order::findOrNew($orderId)->user_id;
});
Return true (or any truthy value) to allow the subscription; return false to deny it. The first parameter is always the authenticated user; subsequent parameters match the wildcards in the channel name.

Route model binding in channels

Use the model type instead of an ID and Laravel resolves it automatically:
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
    return $user->id === $order->user_id;
});

Channel classes

When your channels.php grows, move authorization logic into dedicated classes:
php artisan make:channel OrderChannel
<?php

namespace App\Broadcasting;

use App\Models\Order;
use App\Models\User;

class OrderChannel
{
    public function join(User $user, Order $order): bool
    {
        return $user->id === $order->user_id;
    }
}
Register it in routes/channels.php:
use App\Broadcasting\OrderChannel;

Broadcast::channel('orders.{order}', OrderChannel::class);

Firing broadcast events

Fire a broadcast event the same way you fire any other Laravel event:
use App\Events\OrderStatusUpdated;

OrderStatusUpdated::dispatch($order);
Exclude the current user from receiving their own broadcast (useful in chat or collaborative editing):
broadcast(new OrderStatusUpdated($order))->toOthers();
toOthers() requires the event class to use the InteractsWithSockets trait.

Receiving events with Laravel Echo

Install Echo and set up the client

npm install --save-dev laravel-echo pusher-js
Configure Echo in resources/js/bootstrap.js:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});
Build your assets after updating the config:
npm run build

Listening for events

// Public channel — no authentication required
Echo.channel('announcements')
    .listen('AnnouncementPublished', (e) => {
        console.log(e.announcement);
    });

// Private channel — requires authorization
Echo.private(`orders.${orderId}`)
    .listen('OrderStatusUpdated', (e) => {
        document.getElementById('order-status').textContent = e.status;
    });

// Presence channel — track who's online
Echo.join(`chat.${roomId}`)
    .here((users) => {
        console.log('Currently in room:', users);
    })
    .joining((user) => {
        console.log(user.name, 'joined');
    })
    .leaving((user) => {
        console.log(user.name, 'left');
    })
    .listen('MessagePosted', (e) => {
        appendMessage(e.message);
    });

React and Vue hooks

If you’re using a React or Vue starter kit, Echo ships composable hooks:
// React
import { useEcho, useEchoPublic } from '@laravel/echo-react';

// Private channel
useEcho(`orders.${orderId}`, 'OrderStatusUpdated', (e) => {
    setStatus(e.status);
});

// Public channel
useEchoPublic('announcements', 'AnnouncementPublished', (e) => {
    addAnnouncement(e.announcement);
});
The hook unsubscribes automatically when the component unmounts.

End-to-end example: real-time order status

1

Enable broadcasting and start services

php artisan install:broadcasting --reverb
php artisan reverb:start
php artisan queue:work
2

Create the broadcast event

php artisan make:event OrderStatusUpdated
<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class OrderStatusUpdated implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public function __construct(public Order $order) {}

    public function broadcastOn(): PrivateChannel
    {
        return new PrivateChannel('orders.' . $this->order->id);
    }

    public function broadcastWith(): array
    {
        return [
            'order_id' => $this->order->id,
            'status'   => $this->order->status,
        ];
    }
}
3

Authorize the channel

// routes/channels.php
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('orders.{order}', function (User $user, Order $order) {
    return $user->id === $order->user_id;
});
4

Fire the event when the order changes

use App\Events\OrderStatusUpdated;

$order->update(['status' => 'shipped']);

OrderStatusUpdated::dispatch($order);
5

Listen for the event in the browser

Echo.private(`orders.${orderId}`)
    .listen('OrderStatusUpdated', (e) => {
        document.getElementById('status-badge').textContent = e.status;
    });
Verify your VITE_REVERB_* environment variables are set before running npm run build. Vite inlines these values at build time — they won’t be updated by changing .env after the build.

Next steps

Laravel Reverb

Set up and operate the Reverb server: production configuration, Nginx proxying, Supervisor, and horizontal scaling with Redis.

Events and listeners

Broadcasting is built on Laravel’s event system. Learn how to create events and listeners.
Last modified on April 13, 2026