I’m always excited to take on new projects and collaborate with innovative teams to build impactful digital solutions.

Social Links

Building Real-Time Applications with Laravel Reverb & Livewire: WebSockets Made Simple

Learn how to build real-time features like live notifications, chat, and dashboards using Laravel Reverb, Livewire, and Echo with practical examples.

Introduction

Real-time functionality is no longer a luxury - users expect live updates, instant notifications, and collaborative features. Laravel's ecosystem offers a complete real-time stack: Reverb (a blazing-fast WebSocket server written in PHP), Livewire for reactive components, and Echo for client-side listening.

Understanding the Real-Time Stack

Laravel Reverb is a first-party WebSocket server that works seamlessly with Laravel's event broadcasting. It's built on ReactPHP and offers impressive performance - handling thousands of concurrent connections with minimal resources.

Setting Up Laravel Reverb

Installation

composer require laravel/reverb
php artisan reverb:install
npm install --save-dev laravel-echo pusher-js

Configuration

# .env
BROADCAST_CONNECTION=reverb
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret

Starting the Server

php artisan reverb:start

# With custom options
php artisan reverb:start --host=0.0.0.0 --port=9000

Creating an Event

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

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

    public function __construct(
        public string $message,
        public int $orderId,
    ) {}

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

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

Broadcasting the Event

event(new NewOrderNotification('New order received!', $order->id));

// Or dispatch to a private channel
broadcast(new NewOrderNotification('...', $order->id))->toOthers();

Private Channels with Authorization

// routes/channels.php
use App\Models\User;

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

Client-Side with Echo

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

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

// Listen on a public channel
window.Echo.channel('orders')
    .listen('.order.created', (e) => {
        console.log('New order:', e.message);
        // Update UI
    });

// Listen on a private channel
window.Echo.private(`user.${userId}`)
    .notification((notification) => {
        // Handle broadcast notification
    });

Real-Time Notifications

Combine broadcasting with Laravel's notification system:

use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Notifications\Notification;

class OrderShipped extends Notification implements ShouldBroadcast
{
    use Queueable;

    public function __construct(public Order $order) {}

    public function via($notifiable): array
    {
        return ['database', 'broadcast'];
    }

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

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

Livewire: Real-Time Without JavaScript

Livewire makes building reactive interfaces incredibly simple. Here's a real-time order counter:

<?php

namespace App\Livewire;

use App\Models\Order;
use Livewire\Attributes\On;
use Livewire\Component;

class OrderCounter extends Component
{
    public int $count = 0;

    public function mount(): void
    {
        $this->count = Order::count();
    }

    #[On('echo:orders,order.created')]
    public function increment(): void
    {
        $this->count = Order::count();
    }

    public function render()
    {
        return view('livewire.order-counter');
    }
}
<div class="p-4 bg-white rounded shadow">
    <h3>Total Orders</h3>
    <p class="text-3xl font-bold">{{ $count }}</p>
</div>

Building a Live Chat

// MessageSent event
class MessageSent implements ShouldBroadcast
{
    public function __construct(
        public string $message,
        public string $user,
        public int $roomId,
    ) {}

    public function broadcastOn(): array
    {
        return [new PrivateChannel('chat.' . $this->roomId)];
    }
}

// ChatComponent
class ChatComponent extends Component
{
    public int $roomId;
    public string $message = '';
    public array $messages = [];

    #[On('echo-private:chat.{roomId},message.sent')]
    public function onMessage($payload): void
    {
        $this->messages[] = $payload;
    }

    public function send(): void
    {
        broadcast(new MessageSent($this->message, auth()->user()->name, $this->roomId));
        $this->message = '';
    }

    public function render()
    {
        return view('livewire.chat-component');
    }
}

Presence Channels

Presence channels show who's currently online:

Broadcast::channel('presence.game.{gameId}', function ($user, $gameId) {
    return $user->only(['id', 'name']);
});
window.Echo.join(`game.${gameId}`)
    .here((users) => {
        console.log('Online users:', users);
    })
    .joining((user) => {
        console.log('User joined:', user.name);
    })
    .leaving((user) => {
        console.log('User left:', user.name);
    });

Production Deployment

Supervisor for Reverb

[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/www/html/storage/logs/reverb.log

Nginx WebSocket Proxy

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_read_timeout 60s;
    }
}

Scaling Considerations

  • Reverb supports horizontal scaling with Redis
  • Use Redis as the queue driver for broadcasting
  • Monitor connection counts and memory usage
  • Consider SSL termination at the load balancer
  • Implement reconnection logic in Echo

Conclusion

With Laravel Reverb and Livewire, you can build production-grade real-time applications entirely in PHP. No Node.js servers required. Start with simple notifications, then graduate to chat, dashboards, and collaborative features. The stack scales beautifully and the developer experience is unmatched.

5 min read
Aug 08, 2026
By Developer Shadin
Share

Leave a comment

Your email address will not be published. Required fields are marked *