Hello! Welcome back.
In our last lesson, we laid all the essential groundwork by configuring your Laravel application, the Reverb WebSocket server, and the Laravel Echo client. You now have a live, persistent communication pipeline between your backend and the browser, and you know how to verify that it's working.
Today, we'll put that pipeline to work. This lesson focuses on the core of broadcasting: sending events to specific channels. We'll explore the critical difference between public channels, which are open to anyone, and private channels, which are secure and require authorization. Mastering this distinction is fundamental to building robust, real-time features.
By the end of this 60-minute lesson, you will be able to broadcast events to public and private channels, controlling who receives your real-time updates.
1. Understanding Broadcast Channels
Before we write any code, let's solidify the concept of channels. Think of your WebSocket server as a radio tower. A channel is like a specific frequency. When you broadcast a message (an event) on a frequency, only the clients tuned into that same frequency will receive it.
Laravel provides three types of channels, each serving a different purpose.
Laravel Reverb: A Comprehensive Guide to Real-Time Broadcasting
This article from the Twilio blog gives a concise and clear explanation of the three channel types. It's a great way to build a mental model before we dive into the implementation.
Read the sub-section titled 'Broadcasting Channels' and the three channel types it describes: Public, Private, and Presence. Focus on the analogy and the use case for each.
To summarize:
- Public Channels: Open to all. Perfect for broadcasting information that isn't sensitive, like a public announcement or a live score update on a sports site.
- Private Channels: Require authorization. Only authenticated users who pass a specific check can listen. This is essential for user-specific notifications, private messages, or updates to a user's own data.
- Presence Channels: A special type of private channel that is "presence-aware." They not only require authorization but also keep track of who is currently subscribed to the channel. Ideal for chat rooms or "who's online" lists.
Let's see how to implement the two most common types: public and private.
2. Broadcasting to Public Channels
Public channels are the simplest to implement because they don't require any authorization. Let's create an event and broadcast it to a public channel.
Step 1: Define the Broadcast Event
First, generate an event class using Artisan. Let's imagine an event for when a new blog post is published.
php artisan make:event BlogPostPublished
Now, open app/Events/BlogPostPublished.php and make two crucial changes:
- Implement the
Illuminate\Contracts\Broadcasting\ShouldBroadcastinterface. This tells Laravel that this event should be broadcast. - Define the
broadcastOn()method. This method must return the channel(s) the event will be sent to.
Here's what the modified class will look like. We'll pass the post's data through the constructor.
<?php
namespace App\Events;
use App\Models\Post; // Assuming you have a Post model
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class BlogPostPublished implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public Post $post
) {}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new Channel('posts'),
];
}
}
The key line is new Channel('posts'). We are telling Laravel to broadcast this event on a public channel named posts. Any public properties on the event class (like our $post property) will be automatically serialized and sent as the event's data payload.
Step 2: Dispatch the Event
Now, whenever you create a new post in your application (e.g., in your PostController), you can dispatch the event:
use App\Events\BlogPostPublished;
// ... inside a controller method after a post is saved
$post = Post::create([...]);
BlogPostPublished::dispatch($post);
That's it for the backend! Because you implemented ShouldBroadcast, Laravel's event dispatcher automatically sends this to your queue to be broadcast on the posts channel via Reverb.
On the frontend, you would then use Echo.channel('posts').listen(...) to receive it. We'll cover that in detail in the next lesson.
3. Broadcasting to Private Channels
Private channels are where the real power lies for personalized, real-time experiences. They introduce a critical step: Authorization.
Imagine you have an Order and you want to send status updates. Only the user who owns that order should receive those updates.

Let's walk through the implementation.
Step 1: Define the Private Channel in the Event
First, let's create an OrderStatusUpdated event.
php artisan make:event OrderStatusUpdated
Inside app/Events/OrderStatusUpdated.php, we'll again implement ShouldBroadcast. However, in the broadcastOn() method, we'll instantiate a PrivateChannel instead of a Channel.
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; // Use PrivateChannel
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderStatusUpdated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public Order $order
) {}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
// Note the dynamic {id} parameter in the channel name
return [
new PrivateChannel('orders.'.$this->order->id),
];
}
}
Notice the channel name: 'orders.'.$this->order->id. We are creating a unique, private channel for each specific order. This is a common and powerful pattern.
Step 2: Authorize the Channel
When your frontend tries to listen to orders.123, Laravel Echo will first make an HTTP request to your application to ask: "Is the currently logged-in user allowed to listen to this channel?"
You define the logic for this answer in routes/channels.php.
Broadcasting - Authorizing Channels
The official Laravel documentation provides the definitive guide to authorizing channels. Pay close attention to the Broadcast::channel method and its callback.
Read the section titled 'Authorizing Channels'. This section explains how to use the routes/channels.php file to define your authorization logic. Focus on the structure of the Broadcast::channel method, its arguments (the user and the wildcard parameters), and the boolean it must return.
Let's add our authorization rule to routes/channels.php:
<?php
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;
// The wildcard {orderId} matches the number from the channel name 'orders.123'
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
// Find the order from the database
$order = Order::find($orderId);
// Return true if the authenticated user's ID matches the order's user_id
// Otherwise, return false
return $user->id === $order->user_id;
});
This callback is the gatekeeper.
- Laravel automatically provides the currently authenticated
$user. If the visitor is not logged in, the request is automatically rejected with a403 Forbiddenstatus before this callback is even run. - The
{orderId}from the channel name is passed as the second argument. - You return
trueorfalsebased on your application's logic.
Step 3: See it in Action
This video from the official Laravel channel provides a clear, practical demonstration of setting up a private channel and its authorization.
Watch this demonstration to see how the event, the private channel definition, and the authorization callback all work together.
Watch the segment from 03:13 to 04:28. The presenter implements the ShouldBroadcast interface, defines a private channel in broadcastOn, and then writes the authorization logic in routes/channels.php. This is a perfect visual summary of the steps we just took.
To complete the picture, on the frontend you would use Echo.private('orders.123').listen(...) to subscribe. When you do this, Echo handles the authorization request for you. If the authorization fails, the listen callback will never be triggered.
4. An Introduction to Presence Channels
Presence channels are a powerful enhancement to private channels. They do everything a private channel does, but they also maintain a list of who is currently "present" in the channel.
The changes required are small but significant:
- In your event, return an instance of
PresenceChannelinstead ofPrivateChannel. - In your
channels.phpauthorization callback, instead of returningtrue, you return an array of data about the user if they are authorized. This data is what other members of the channel will see. If they are not authorized, you returnnullorfalse.
// In routes/channels.php for a presence channel
Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) {
if ($user->canJoinRoom($roomId)) {
// Return user data if authorized
return ['id' => $user->id, 'name' => $user->name];
}
// Return null or false if not authorized
return null;
});
On the frontend, instead of private(), you use join(), which gives you access to special events like here, joining, and leaving.
This video provides an excellent walkthrough of converting a private channel to a presence channel and demonstrates these special frontend events.
Ep50 - Private and Presence Channels
This video from Acadea.io shows a practical example of building a chat feature with a presence channel. It clearly explains the backend and frontend changes needed.
Watch the segment from 06:12 to 10:22. Focus on: How the channels.php authorization callback is changed to return the user instance. How the frontend code uses Echo.join() instead of Echo.private(). The purpose of the here, joining, and leaving methods for managing a list of online users.
5. A Handy Tool: Listing Your Channels
As your application grows, you might have many channel authorization routes defined. Laravel provides a useful Artisan command to see them all at once.
php artisan channel:list

Conclusion
You now have the knowledge to direct your real-time events to the correct audience. This control is the key to building sophisticated, secure, and interactive features.
Key Takeaways:
- Public Channels are created with
new Channel('name')and require no authorization. They are for broadcasting to everyone. - Private Channels are created with
new PrivateChannel('name.{id}')and require an authorization callback inroutes/channels.phpthat returnstrueorfalse. They are for broadcasting to specific, authenticated users. - Presence Channels are a type of private channel that also tracks who is subscribed. Their authorization callback must return user data instead of a simple boolean.
- The
php artisan channel:listcommand is a helpful tool for inspecting your registered channel routes.
Up Next:
We've mastered the backend—defining events and authorizing channels to control who can listen. In the next lesson, "Configure Laravel Echo on the frontend to listen for broadcasted events," we will shift our focus entirely to the client-side. You'll learn how to use Laravel Echo to subscribe to these channels, listen for events, and dynamically update your UI with the data you receive.