Skip to main content
WireChat provides real-time notifications to keep users informed of new messages and conversation activity. Notifications are delivered via Laravel’s broadcasting system and can include web push notifications.

Broadcasting Setup

Enable broadcasting in your panel configuration:
use Wirechat\Wirechat\Panel;

Panel::make('admin')
    ->broadcasting()  // Enable real-time broadcasting
    // ... other configuration

Configure Broadcasting Channels

Specify your broadcasting driver:
// config/broadcasting.php
'default' => env('BROADCAST_DRIVER', 'pusher'),

'connections' => [
    'pusher' => [
        'driver' => 'pusher',
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'app_id' => env('PUSHER_APP_ID'),
        'options' => [
            'cluster' => env('PUSHER_APP_CLUSTER'),
            'useTLS' => true,
        ],
    ],
    
    'reverb' => [
        'driver' => 'reverb',
        'key' => env('REVERB_APP_KEY'),
        'secret' => env('REVERB_APP_SECRET'),
        'app_id' => env('REVERB_APP_ID'),
        'options' => [
            'host' => env('REVERB_HOST'),
            'port' => env('REVERB_PORT', 443),
            'scheme' => env('REVERB_SCHEME', 'https'),
        ],
    ],
],
Laravel Reverb is the recommended broadcasting driver for new projects. It’s included with Laravel and requires no external services.

Message Notifications

When a message is sent, WireChat dispatches notifications to all participants:
// src/Livewire/Chat/Chat.php:673-680
try {
    // Broadcast message created event
    broadcast(new MessageCreated($message, $this->panel()->getId()))->toOthers();
    
    // Notify participants if conversation is NOT self
    if (!$this->conversation->isSelf()) {
        NotifyParticipants::dispatch($this->conversation, $message, $this->panel);
    }
} catch (\Throwable $th) {
    Log::error($th->getMessage());
}

Notification Channels

WireChat creates private channels for each participant:
// Participant channel format
{$panelId}.participant.{$encodedType}.{$userId}

// Conversation channel format
{$panelId}.conversation.{$conversationId}

Listening for Notifications

Components automatically listen for relevant events:
// src/Livewire/Chats/Chats.php:95-96
$channelName = "$panelId.participant.$encodedType.$userId";
$listeners["echo-private:{$channelName},.Wirechat\\Wirechat\\Events\\NotifyParticipant"] = 'refreshComponent';

NewMessageNotification

The notification class broadcasts new messages:
// src/Notifications/NewMessageNotification.php
namespace Wirechat\Wirechat\Notifications;

class NewMessageNotification extends Notification implements ShouldBroadcastNow
{
    public $message;
    
    public function __construct(Message $message)
    {
        $this->message = $message;
    }
    
    public function via(object $notifiable): array
    {
        return ['broadcast'];
    }
    
    public function toBroadcast($notifiable)
    {
        return new BroadcastMessage([
            'message_id' => $this->message->id,
            'conversation_id' => $this->message->conversation_id,
        ]);
    }
}
The notification implements ShouldBroadcastNow to ensure messages are sent immediately, not queued.

Web Push Notifications

Enable browser push notifications:
Panel::make('admin')
    ->webPushNotifications()  // Enable web push
    ->serviceWorkerPath(asset('sw.js'))  // Optional: custom path

Service Worker Setup

Create a service worker file in your public directory:
// public/sw.js
self.addEventListener('push', function(event) {
    const data = event.data.json();
    
    const options = {
        body: data.body,
        icon: data.icon || '/icon.png',
        badge: '/badge.png',
        vibrate: [200, 100, 200],
        data: {
            conversationId: data.conversation_id,
            url: data.url
        }
    };
    
    event.waitUntil(
        self.registration.showNotification(data.title, options)
    );
});

self.addEventListener('notificationclick', function(event) {
    event.notification.close();
    event.waitUntil(
        clients.openWindow(event.notification.data.url)
    );
});

Default Service Worker Path

// src/Panel/Concerns/HasWebPushNotifications.php:42-44
if ($this->serviceWorkerPath === null) {
    $this->serviceWorkerPath(asset('sw.js'));
}

NotifyParticipants Job

Messages are broadcast to participants via a queued job:
use Wirechat\Wirechat\Jobs\NotifyParticipants;

// Dispatch notification job
NotifyParticipants::dispatch($conversation, $message, $panel);
This job:
  • Gets all participants except the sender
  • Sends notification to each participant
  • Respects user notification preferences

Broadcasting Events

MessageCreated Event

Broadcasts when a new message is sent:
namespace Wirechat\Wirechat\Events;

class MessageCreated implements ShouldBroadcastNow
{
    public function __construct(
        public Message $message,
        public string $panelId
    ) {}
    
    public function broadcastOn(): array
    {
        return [
            new PrivateChannel(
                "{$this->panelId}.conversation.{$this->message->conversation_id}"
            )
        ];
    }
}

MessageDeleted Event

Broadcasts when a message is removed:
namespace Wirechat\Wirechat\Events;

class MessageDeleted implements ShouldBroadcastNow
{
    public function __construct(
        public Message $message,
    ) {}
    
    public function broadcastOn(): array
    {
        return [
            new PrivateChannel(
                "{$this->panelId}.conversation.{$this->message->conversation_id}"
            )
        ];
    }
}

Handling Incoming Notifications

Livewire components automatically handle notification events:
// src/Livewire/Chat/Chat.php:138-168
public function appendNewMessage($event)
{
    // Verify message belongs to this conversation
    if ($event['message']['conversation_id'] == $this->conversation->id) {
        $newMessage = Message::find($event['message']['id']);
        
        // Ignore own messages
        if ($newMessage->ownedBy(auth()->user())) {
            return null;
        }
        
        // Add message to view
        $this->pushMessage($newMessage);
        
        // Mark as read
        $this->conversation->markAsRead();
        
        // Refresh chat list
        $this->dispatch('refresh')->to(Chats::class);
    }
}

Unread Count

Track unread messages per user:
// Get unread count for a conversation
$unreadCount = $conversation->getUnreadCountFor($user);

// Get total unread across all conversations
$totalUnread = $user->getUnreadCount();

// Get unread messages
$unreadMessages = $conversation->unreadMessages($user);

Read Receipts

Mark conversations as read:
// Mark as read for current user
$conversation->markAsRead($user);

// Check if user has read the conversation
if ($conversation->readBy($user)) {
    // All messages have been read
}

Read Tracking

Participants have a conversation_read_at timestamp:
// src/Models/Conversation.php:519
public function markAsRead(?Model $user = null)
{
    $user = $user ?? auth()->user();
    
    if ($user == null) {
        return null;
    }
    
    $this->participant($user)?->update([
        'conversation_read_at' => now()
    ]);
}

Broadcasting Configuration

Configure broadcasting behavior:
Panel::make('admin')
    ->broadcasting()
    ->broadcastingAuthRoute('custom/broadcasting/auth')  // Custom auth route

Notification Preferences

Implement user notification preferences:
// In your User model
public function shouldReceiveNotificationFor(Conversation $conversation): bool
{
    // Check user preferences
    if ($this->notification_settings['mute_all'] ?? false) {
        return false;
    }
    
    // Check if conversation is muted
    if ($this->mutedConversations()->where('id', $conversation->id)->exists()) {
        return false;
    }
    
    return true;
}

Error Handling

WireChat gracefully handles broadcasting failures:
try {
    broadcast(new MessageCreated($message, $this->panel()->getId()))->toOthers();
} catch (\Throwable $th) {
    Log::error($th->getMessage());
    // Message is still saved, notification just failed
}
If broadcasting fails, messages are still saved to the database. Users will see them when they refresh or load the conversation.

Testing Notifications

Test notification delivery:
use Illuminate\Support\Facades\Notification;

/** @test */
public function it_notifies_participants_of_new_messages()
{
    Notification::fake();
    
    $sender = User::factory()->create();
    $receiver = User::factory()->create();
    
    $conversation = $sender->createConversationWith($receiver);
    $message = $sender->sendMessageTo($conversation, 'Hello!');
    
    Notification::assertSentTo(
        $receiver,
        NewMessageNotification::class
    );
}

Debugging Notifications

Enable Laravel Echo debugging:
// resources/js/bootstrap.js
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    forceTLS: true,
    enabledTransports: ['ws', 'wss'],
    
    // Enable debugging
    logToConsole: true,
});

Best Practices

Queue Configuration

Ensure your queue worker is running for reliable notification delivery

Broadcasting Driver

Use Laravel Reverb for easy setup or Pusher for production scale

Error Logging

Monitor logs for broadcasting failures and adjust queue settings

User Preferences

Respect user mute and notification preference settings

Next Steps

Broadcasting

Configure Laravel broadcasting for your application

Private Chats

Learn about read receipts in private conversations

Group Chats

Understand notification behavior in groups

Events

Work with WireChat events and listeners

Build docs developers (and LLMs) love