How to Build Real-Time Features with Laravel Reverb

We earn commissions when you shop through the links below.

If you’ve been looking for a clean, first-party solution to add live updates, notifications, or collaborative features to your Laravel app, learning how to build real-time features with Laravel Reverb is the right move. Reverb is Laravel’s own WebSocket server — no third-party dependency like Pusher required — and it integrates seamlessly with Laravel’s broadcasting system you already know.

In this guide, I’ll walk you through setup, broadcasting events, and consuming them on the frontend with Echo. By the end, you’ll have a working real-time feature you can extend for any use case.

What Is Laravel Reverb?

Reverb is a first-party WebSocket server for Laravel applications, built in PHP and designed to slot directly into Laravel’s event broadcasting infrastructure. Before Reverb, you’d either pay for Pusher or run your own Soketi instance. Reverb eliminates that choice — it’s self-hosted, open source, and built specifically for Laravel.

It supports public channels, private channels, and presence channels, which means it covers everything from simple live counters to complex multi-user collaborative tools.

Prerequisites

  • Laravel 11 or later
  • PHP 8.2+
  • Node.js (for the frontend)
  • A Redis instance (recommended for production queue handling)

Step 1: Install Laravel Reverb

Run the Reverb installer via Artisan:

php artisan install:broadcasting

This command installs the laravel/reverb package, publishes the Reverb config, and sets up your .env with the necessary keys. You’ll see new variables added automatically:

BROADCAST_CONNECTION=reverb

REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-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}"

The installer also installs Laravel Echo and the Pusher JS client (which Echo uses under the hood, even with Reverb). Run npm install if it doesn’t do so automatically.

Step 2: Start the Reverb Server

During development, start Reverb with:

php artisan reverb:start

You should see output confirming it’s listening on 0.0.0.0:8080. Keep this running in a separate terminal alongside your queue worker, since broadcasting relies on queued jobs:

php artisan queue:listen

Step 3: Create a Broadcastable Event

Let’s build a practical example — a live notification when a new order is placed. Generate the event:

php artisan make:event OrderPlaced

Edit the generated event class:

<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderPlaced implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(public Order $order)
    {}

    public function broadcastOn(): array
    {
        return [
            new Channel('orders'),
        ];
    }

    public function broadcastAs(): string
    {
        return 'order.placed';
    }

    public function broadcastWith(): array
    {
        return [
            'id' => $this->order->id,
            'total' => $this->order->total,
            'customer' => $this->order->customer_name,
        ];
    }
}

The key points: implement ShouldBroadcast, define your channel in broadcastOn(), and use broadcastWith() to control exactly what data gets sent to the client. Never broadcast raw Eloquent models — be explicit about what you expose.

Step 4: Dispatch the Event

From your controller or service, dispatch the event as you normally would:

use App\Events\OrderPlaced;

// Inside your controller method
$order = Order::create($validatedData);

OrderPlaced::dispatch($order);

That’s it on the PHP side. Laravel will handle queuing the broadcast job and Reverb will push it to connected clients.

Step 5: Set Up Laravel Echo on the Frontend

The install:broadcasting command scaffolds a resources/js/echo.js file (or adds config to bootstrap.js). Make sure it looks like this:

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 ?? 8080,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

Now listen for your event in any component or page script:

window.Echo.channel('orders')
    .listen('.order.placed', (data) => {
        console.log('New order!', data);
        // Update your UI — add to a list, show a toast, etc.
        showNotification(`Order #${data.id} placed by ${data.customer}`);
    });

Note the leading dot in .order.placed — this is required when using a custom broadcastAs() name.

Step 6: Private and Presence Channels

For user-specific notifications, switch to a private channel. Update your event:

use Illuminate\Broadcasting\PrivateChannel;

public function broadcastOn(): array
{
    return [
        new PrivateChannel('orders.' . $this->order->user_id),
    ];
}

Define the channel authorization in routes/channels.php:

use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('orders.{userId}', function ($user, $userId) {
    return (int) $user->id === (int) $userId;
});

On the frontend, subscribe with private() instead of channel():

window.Echo.private(`orders.${currentUserId}`)
    .listen('.order.placed', (data) => {
        // Only this user receives this event
    });

Presence channels work similarly but also track who’s online in the channel — great for showing “users currently viewing” indicators in collaborative apps.

Deploying Reverb to Production

In production, you need to run Reverb as a persistent process. Use Supervisor to keep it alive:

[program:reverb]
command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/reverb.log

You’ll also want to proxy WebSocket connections through Nginx:

location /app/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

For hosting, DigitalOcean droplets work well for Reverb deployments — you get full control over your server config, Supervisor, and Nginx setup without the abstraction getting in the way. If you’d prefer a more hands-off deployment experience, Railway supports running Laravel apps with custom start commands, so you can deploy both your app and Reverb in the same project.

Set REVERB_SCHEME=https and update your Vite env vars to point to your production domain. Make sure your SSL certificate covers WebSocket connections — Let’s Encrypt works fine here.

Performance Tips

  • Use Redis as your queue driver in production. Broadcasting events go through the queue, so a fast queue backend matters at scale.
  • Horizontal scaling: Reverb supports scaling via Redis pub/sub. Set REVERB_SCALING_ENABLED=true in your env and configure the Redis connection.
  • Don’t broadcast sensitive data: Always use broadcastWith() to whitelist fields explicitly.
  • Debounce client-side listeners when events fire frequently to avoid hammering the DOM.

Wrapping Up

Knowing how to build real-time features with Laravel Reverb means you can ship live dashboards, instant notifications, collaborative editors, and chat systems without leaving the Laravel ecosystem or paying for external WebSocket services. The integration with Laravel’s existing broadcasting API is seamless — if you’ve used Pusher with Laravel before, the mental model transfers directly.

The full picture: install Reverb, implement ShouldBroadcast on your events, configure Echo on the frontend, and handle authorization via channel routes. That’s genuinely all there is to it for most use cases. For deeper dives into Laravel’s broadcasting system and advanced queue configuration, Udemy has solid Laravel courses that cover these topics in detail.

Start with a simple public channel to get the feedback loop working, then layer in private channels and presence as your feature requirements grow.