Cross-component event communication in Livewire with $emit and $dispatch
Livewire's $emit and $dispatch let components talk vertically without JavaScript, but ancestor listeners and state bloat require careful handling.
29 Aug 2026, 17:10 UTC

Problem: Sending events between Livewire components without writing JavaScript
In a typical Livewire app a child component may need to notify an ancestor about a state change. Without the event API you would reach for custom AJAX or a global state container. Livewire provides $emit and $dispatch to propagate events through the framework's internal event bus preserving component state and eliminating manual requests.
Thesis: $emit pushes events upward; $dispatch reaches the root
Components can $emit('event-name', $data) to fire an event that parent components listen for via $listeners. A child may also $dispatch('event-name') to send an event all the way to the root, where any matching $listeners in ancestor components receive it. The mechanism relies on Livewire's internal event bus and works across Livewire 2.x and 3.x with only minor argument-handling differences.
Worked example: child emits, parent listens
namespace App\Http\Livewire; use Livewire\Component; class StatusBadge extends Component { public string $status = 'inactive'; public function markActive() { $this->status = 'active'; $this->emit('status-changed', $this->status); public function render() { return view('livewire.status-badge'); } }
The $emit call triggers a livewire/action POST behind the scenes. The framework patches only the DOM nodes that need updating without a full page reload.
Listening in an ancestor
In a parent component declare the event with the $listeners property. When the child emits status-changed the parent's handling method runs and can update its own state.
namespace App\Http\Livewire; use Livewire\Component; class Dashboard extends Component { public ?string $_status = null; public $listeners = ['status-changed' => '$_status = $event']; public function render() { return view('livewire.dashboard'); } }
The $listeners array maps event names to inline PHP. The $event variable holds whatever the child emit passed—in this case the new status string.
Going further: $dispatch to the root
If you need an event to bypass intermediate components and reach the topmost ancestor use $dispatch instead of $emit. The syntax is identical but Livewire routes the event to every matching $listeners in the component hierarchy starting from the root.
$this->dispatch('global-notification', ['type' => 'success', 'message' => 'Changes saved.']);
Any component in the tree that has $listeners['global-notification'] will receive the payload regardless of nesting depth.
Limitation: listener overhead and state bloat
Two practical concerns come up frequently.
- Explicit $listeners in every ancestor. If a child emits from deep nesting each intermediate component that wants to forward or handle the event must declare a matching $listeners entry. Omitting a declaration means the event stops at that level.
- View state bloat. Passing large arrays or objects via $emit or $dispatch serializes the data into Livewire's view state. Over time this can increase request sizes and slow page loads. Serialize heavy data to a storage backend (database cache) and pass only an identifier instead.
How to check the result practically
- Start your Laravel app: php artisan serve
- Open the page in a browser and open the DevTools Network tab.
- Trigger the child's $emit or $dispatch.
- You should see a livewire/action POST request. Its response contains the DOM patch that Livewire applies.
- Verify that the parent component's rendered output reflects the new event data without a full reload.
Closing: a practical pattern
For most inter-component notifications start with $emit from the child and a matching $listeners declaration in the immediate parent. If the signal needs to travel farther add $listeners declarations in each ancestor or switch to $dispatch and accept the broader reach. Always prefer passing a simple string or ID; serialize complex payloads and store them elsewhere before emitting.
This approach keeps JavaScript out of your Laravel views leverages Livewire's reactive model and when you check the network tab after each change gives you immediate feedback that the event propagated as expected.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.