Description: $dispatch() enables decoupled communication between AlpineJS components. One component can emit a custom event, and other components (whether they are parents, children, siblings, or even unrelated components listening globally) can react to this event. This promotes modularity and reduces direct dependencies between components, making your application easier to manage and scale.
$dispatch('eventName', detailObject)
This is an AlpineJS "magic property" available within your component's scope. It's a function used to emit a custom browser event.
'eventName': A string representing the unique name of your custom event (e.g., 'item-added', 'user-updated').detailObject: An optional JavaScript object containing data you want to send with the event. This payload can be accessed by any component listening for this event. If you don't need to send data, you can omit this argument.By default, events dispatched this way "bubble up" the DOM tree. This means parent elements (and their Alpine components) can listen for events dispatched by their children.
<!-- Component A (Child) -->
<div x-data>
<button @click="$dispatch('notify-parent', { message: 'Hello from child!' })">
Send Notification
</button>
</div>
@event-name.modifier="handler($event.detail)"
This is the AlpineJS syntax (shorthand for x-on:event-name) for listening to DOM events, including custom events dispatched by $dispatch().
event-name: The name of the custom event you want to listen for. It must exactly match the name used in $dispatch()..modifier: Optional event modifiers. Common ones include .window, .document, .once, .prevent, .stop. For custom event communication across disparate components, .window is particularly important.handler($event.detail): The JavaScript expression or component method to execute when the event is caught.
$event: A special Alpine variable representing the native browser Event object.$event.detail: When a custom event is dispatched with a payload (the detailObject), this is where that payload is accessible within the event handler.<!-- Component B (Parent or other listener) -->
<div x-data="{ notification: '' }" @notify-parent="notification = $event.detail.message">
<p>Notification: <span x-text="notification"></span></p>
</div>
.window modifier
When you use @event-name.window, the event listener is attached to the global window object instead of the component's root element. This allows the component to listen for events regardless of where they were dispatched in the DOM, making it essential for communication between sibling components, or components that are not in a direct parent-child relationship, or for truly global event handling.
<!-- Component C (Could be anywhere on the page) -->
<div x-data="{ globalMessage: '' }" @notify-global.window="globalMessage = $event.detail.info">
<p>Global Message: <span x-text="globalMessage"></span></p>
</div>
<!-- Another component dispatching the global event -->
<div x-data>
<button @click="$dispatch('notify-global', { info: 'This is a global alert!' })">
Send Global Alert
</button>
</div>
Using the .window modifier is crucial for global events or when the listening component is not a direct ancestor of the dispatching component.
Python developers might be used to more explicit pub/sub systems. In AlpineJS, if you dispatch an event (e.g., $dispatch('my-event')) and a sibling component or a component in a different part of the DOM tree is meant to listen, it won't hear the event unless its listener is attached to a common ancestor (like document or window). Using @my-event.window="handler()" on the listener ensures it catches the event regardless of its DOM position relative to the dispatcher, as long as the event bubbles up to the window (which custom events do by default).
Event detail ($event.detail) is where the payload sent with $dispatch is found.
When you send data with an event like $dispatch('data-update', { id: 1, status: 'complete' }), beginners might instinctively try to access this data directly on the $event object (e.g., $event.id). However, by web standards for Custom Events, the payload is encapsulated within the detail property of the event object. So, you must use $event.detail.id or $event.detail.status to access the passed data in your handler.
This example demonstrates how a "Product Adder" component can dispatch an item-added event, and a separate "Shopping Cart" component listens for this event (using .window) to update its list of items.
This component dispatches an 'item-added' event with the product name.
This component listens for 'item-added.window' events and updates the list.