skip to content

Broadcast Channels & Echo

ShouldBroadcast events push server-side changes to browsers over public, private and presence channels that Echo subscribes to. Interviewers probe channel authorization and excluding the sender.

on this pageshow

explore

questions

5

In Laravel 13, what steps does it take to broadcast a server-side event such as BidPlaced so that browsers receive it through Echo?

level: juniorimportance: must knowfreq 55%

answer

  1. broadcasting is opt-in
  2. install:broadcasting writes channels.php
  3. ShouldBroadcast needs broadcastOn()
  4. a queue worker sends it
  5. Echo listen or useEcho

basics

~20 s

Run php artisan install:broadcasting, implement ShouldBroadcast on the event with a broadcastOn() returning its channels, dispatch it as usual with a queue worker running, and subscribe in the browser with Echo's channel()->listen() or a useEcho hook.

solid answer

~30 s

Broadcasting is **off** in a new Laravel 13 app. `php artisan install:broadcasting` (optionally `--reverb`) publishes `config/broadcasting.php`, creates `routes/channels.php`, adds `channels:` to `withRouting()` in `bootstrap/app.php`, sets `BROADCAST_CONNECTION` in `.env` and installs `laravel-echo` with `pusher-js`. The event then implements `ShouldBroadcast` and defines `broadcastOn()`, returning a `Channel`, `PrivateChannel` or `PresenceChannel` such as `new Channel('auctions.'.$this->bid->auction_id)`. You dispatch it like any event, `BidPlaced::dispatch($bid)`; the dispatcher pushes a broadcast job, so a **queue worker** must run. In the browser, `Echo.channel('auctions.42').listen('BidPlaced', e => ...)` or, in the React, Vue and Svelte stacks, `useEcho` (private by default) or `useEchoPublic` receives the payload.

code

php · 22 lines
php
<?php

namespace App\Events;

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

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

    public function __construct(public Bid $bid) {}

    public function broadcastOn(): Channel
    {
        return new Channel('auctions.'.$this->bid->auction_id);
    }
}

go deeper

for a junior

Recall the four steps: install:broadcasting, ShouldBroadcast with broadcastOn(), dispatch with a worker running, and Echo or useEcho in the browser.

for a middle

Explain what install:broadcasting changes in the skeleton, how the three channel classes map to wire names, and why the queued broadcast job needs a worker.

for a senior

Diagnose a silent broadcast pipeline end to end: driver setting, queue worker, WebSocket server, channel and event names on both sides.

for a principal

Decide when realtime pushes justify running a WebSocket tier at all versus polling, and which driver fits the team's hosting and operations budget.

## What broadcasting does **Broadcasting** sends a server-side Laravel event to JavaScript running in the browser over a WebSocket, so the page updates without polling. On an auction site, when someone places a bid, every visitor watching that auction should see the new highest bid immediately. Laravel does this with three pieces: - a **broadcast driver** on the server side (Reverb, Pusher Channels, Ably, Mercure, or `log`/`null` for development and tests) - **channels**, named streams the browser subscribes to - **Laravel Echo**, the JavaScript client that subscribes to channels and receives events ## Step 1: turn it on Broadcasting is **opt-in** in the Laravel 13 skeleton: there is no `config/broadcasting.php` and no `routes/channels.php` until you run: ```bash php artisan install:broadcasting --reverb ``` The command: 1. publishes `config/broadcasting.php` 2. creates `routes/channels.php` for channel authorization callbacks 3. adds `channels: __DIR__.'/../routes/channels.php'` to `withRouting()` in `bootstrap/app.php`, which also registers the `/broadcasting/auth` route 4. writes `BROADCAST_CONNECTION` to `.env` for the chosen driver 5. installs the driver's packages and the Echo client (`laravel-echo` and `pusher-js`, plus `@laravel/echo-react` or `@laravel/echo-vue` in those stacks) Without a flag it prompts for the driver; `--pusher`, `--ably` and `--mercure` pick the others. ## Step 2: mark the event An event becomes broadcastable by implementing `Illuminate\Contracts\Broadcasting\ShouldBroadcast`. The interface requires `broadcastOn()`, which returns one channel or an array of channels: | Class | Who may subscribe | Wire name | |---|---|---| | `Channel` | anyone, no authorization | `auctions.42` | | `PrivateChannel` | authenticated and authorized users | `private-auctions.42` | | `PresenceChannel` | authorized users, who also see each other | `presence-auctions.42` | The `make:event` stub already imports these classes and contains a `broadcastOn()` method, so only `implements ShouldBroadcast` and the channel name need to be written. ## Step 3: dispatch and run a worker The event is fired like any other: `BidPlaced::dispatch($bid)` or `event(new BidPlaced($bid))`. The event dispatcher notices `ShouldBroadcast` and pushes a broadcast job onto the queue, in addition to running any ordinary listeners of the event. A **queue worker** has to pick that job up; with the skeleton's `QUEUE_CONNECTION=database` and no `queue:work` running, nothing ever reaches the browser. A WebSocket server has to be running too, for example Reverb started with `reverb:start`. ## Step 4: listen in the browser With plain Echo: - `Echo.channel('auctions.42').listen('BidPlaced', callback)` for a public channel - `Echo.private(...)` for a private channel, `Echo.join(...)` for a presence channel With the React, Vue or Svelte stacks, the `useEcho` hook subscribes when the component mounts and leaves the channel when it unmounts. Note its default: `useEcho` subscribes to a **private** channel, while `useEchoPublic` and `useEchoPresence` cover the other two kinds. The event name is the PHP class name; Echo prepends the `App.Events` namespace for you, so `'BidPlaced'` matches `App\Events\BidPlaced`. ## Checking the whole pipeline When a broadcast does not show up, walk the chain in the order the data travels: 1. `BROADCAST_CONNECTION` names a real driver such as `reverb`, not the skeleton's `log` 2. the event implements `ShouldBroadcast` and `broadcastOn()` returns the channel the page subscribes to 3. a queue worker is running and the broadcast job is not failing 4. the WebSocket server is running and reachable from the browser on the host and port Echo was built with 5. the browser's developer tools show a subscription to the exact channel name, and the listener uses the exact event name Each link can fail silently, which is why the order matters: checking the browser first tells you little if the job never left the queue. ## What arrives By default the payload is every **public property** of the event, with Eloquent models converted to arrays. Methods such as `broadcastWith()` and `broadcastAs()` reshape the payload and the name when the default is too much or too tied to the PHP class. ## Common mistakes - dispatching the event and expecting delivery with no queue worker running - subscribing with `useEcho` to a channel the server declares public, which makes Echo request authorization for a private channel that nothing is broadcast on - forgetting that `routes/channels.php` is only loaded once `install:broadcasting` has wired it in - leaving `BROADCAST_CONNECTION=log` from the skeleton and wondering why nothing reaches the socket

  • Bids are saved but no browser ever receives BidPlaced; what do you check first?
    Check that a queue worker is running, because `ShouldBroadcast` events are sent by a queued job, and that `BROADCAST_CONNECTION` is not `log` or `null`. Then confirm the WebSocket server is up and that the channel name and event name in Echo match `broadcastOn()` and the class name exactly.
  • Why does useEcho fail on a channel that the server broadcasts as a public Channel?
    `useEcho` defaults to a private subscription, so Echo subscribes to `private-auctions.42` and asks `/broadcasting/auth` to authorize it. Nothing is broadcast on that private name, and without a matching `Broadcast::channel` callback the authorization is refused. Use `useEchoPublic` for a public `Channel`.

saying these in an interview costs you the question

  • Broadcasting works in a new Laravel 13 app without running install:broadcasting.
  • ShouldBroadcast events reach the browser even when no queue worker is running.
  • Echo needs the full App\Events\BidPlaced class name in every listen() call.
  • useEcho subscribes to a public channel unless told otherwise.
  • A broadcast event cannot also have ordinary listeners.
open as a page

In Laravel broadcasting, how are private and presence channels authorized with Broadcast::channel, and what must a presence channel callback return?

level: middleimportance: must knowfreq 50%

basics

~20 s

Echo asks /broadcasting/auth before subscribing; Laravel runs the matching Broadcast::channel callback from routes/channels.php with the authenticated user and wildcard values. Private callbacks return true or false; presence callbacks return an array describing the user, or false/null to refuse.

open as a page

In Laravel, what is the difference between ShouldBroadcast and ShouldBroadcastNow, and when would you pick each for an auction's bid updates?

level: middleimportance: should knowfreq 40%

basics

~20 s

ShouldBroadcast sends the event through a queued job, so a worker delivers it later and the request stays fast. ShouldBroadcastNow extends it and sends immediately inside the current process, adding the broadcaster call to the request but needing no worker.

open as a page

In a Laravel app using Echo, a bidder sees their own bid appear twice; how does broadcast()->toOthers() fix it, and what must be in place?

level: middleimportance: should knowfreq 34%

basics

~20 s

The page adds the bid from its own HTTP response and again from the broadcast. broadcast(new BidPlaced($bid))->toOthers() excludes the sender's socket, read from the X-Socket-ID header; the event needs the InteractsWithSockets trait and the request must carry that header.

open as a page

In Laravel, a BidPlaced broadcast on a public auction channel exposes each bidder's email to every viewer; why does that happen, and how do broadcastWith and broadcastAs help?

level: seniorimportance: should knowfreq 36%

basics

~20 s

By default every public property of the event is broadcast, and an Eloquent model is sent as its toArray() output, loaded relations included. On a public channel anyone can read it. broadcastWith() sends only chosen fields; broadcastAs() renames the event.

open as a page