Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Event Bus

The Event Bus enables different parts of your application to communicate and stay synchronized through message passing. It supports event-driven architecture for drop-ins, allowing containers to react to changes from other containers and communicate data changes to the storefront.

Import

From drop-in project using the SDK:

import { events } from '@adobe-commerce/elsie/lib';

From integration project (storefront):

import { events } from '@dropins/tools/event-bus.js';

Core Methods

Subscribe to Events

Subscribe to events and receive notifications when they occur.

const eventListener = events.on('<event>', (payload) => {
// Handle the event payload
console.log('Event received:', payload);
});
// Stop listening to the event
eventListener.off();

Example:

// Listen for cart updates
const cartListener = events.on('cart/data', (cartData) => {
if (cartData) {
console.log(`Cart has ${cartData.totalQuantity} items`);
updateCartUI(cartData);
} else {
console.log('Cart is empty');
showEmptyCart();
}
});
// Later, when you want to stop listening
cartListener.off();

Emit Events

Broadcast events to all listeners across your application.

events.emit('<event>', payload);

Examples:

// Emit cart data
const cartData = {
id: 'cart-123',
totalQuantity: 2,
items: [
{ uid: 'item-1', quantity: 1, sku: 'PROD-001', name: 'Product Name' }
]
};
events.emit('cart/data', cartData);

Get Last Event Payload

Retrieve the most recent payload for a specific event.

const lastPayload = events.lastPayload('<event>');

Example:

// Get the current cart state without waiting for an event
const currentCart = events.lastPayload('cart/data');
if (currentCart) {
console.log('Current cart total:', currentCart.totalQuantity);
}

Enable Debug Logging

Enable console logging to debug event flow.

// Enable logging to see all events in console
events.enableLogger(true);

Advanced Features

Eager Loading

When subscribing to events, you can execute the event handler immediately with the last known payload. This is useful for getting the current state without waiting for the next event.

// Handler will execute immediately if there's a previous payload
const listener = events.on('cart/data', (cartData) => {
console.log('Cart data received:', cartData);
}, { eager: true });

Use cases:

  • Initialize UI components with current state
  • Avoid waiting for the first event emission
  • Ensure components have the latest data on mount

Event Scoping

Create namespaced events to avoid conflicts between different parts of your application.

// Subscribe to a scoped event
const scopedListener = events.on('data/update', (data) => {
console.log('Scoped data received:', data);
}, { scope: 'feature-a' });
// Emit a scoped event
events.emit('data/update', payload, { scope: 'feature-a' });
// Get last payload for a scoped event
const lastScopedData = events.lastPayload('data/update', { scope: 'feature-a' });

Scoped event names: When using scopes, the actual event name becomes scope/event. For example:

  • 'feature-a/data/update' instead of 'data/update'
  • 'module-b/user/action' instead of 'user/action'

Use cases:

  • Separate different features or modules
  • Different contexts within the same application
  • Component-specific event handling

Combining Options

Use both eager loading and scoping together for powerful event handling.

// Subscribe to a scoped event with eager loading
const listener = events.on('locale', (locale) => {
console.log('Current locale:', locale);
}, {
eager: true,
scope: 'user-preferences'
});

Event-Driven Drop-ins

The Event Bus enables drop-ins to be truly event-driven, allowing for loose coupling between components and seamless communication across the application.

Container-to-Container Communication

Containers can react to changes from other Containers, enabling complex interactions without direct dependencies.

// Product Container: Emits when a product is added to cart
function ProductContainer() {
const handleAddToCart = (product) => {
// Add to cart logic...
// Notify other containers about the cart change
events.emit('cart/data', updatedCartData);
};
return (
<button onClick={() => handleAddToCart(product)}>
Add to Cart
</button>
);
}
// Cart Container: Reacts to cart changes from any source
function CartContainer() {
useEffect(() => {
const cartListener = events.on('cart/data', (cartData) => {
updateCartDisplay(cartData);
updateCartBadge(cartData.totalQuantity);
}, { eager: true });
return () => cartListener.off();
}, []);
return <CartDisplay />;
}
// Mini Cart Container: Also reacts to the same cart changes
function MiniCartContainer() {
useEffect(() => {
const cartListener = events.on('cart/data', (cartData) => {
updateMiniCart(cartData);
}, { eager: true });
return () => cartListener.off();
}, []);
return <MiniCart />;
}

Storefront Communication

Drop-ins can communicate data changes to the storefront, enabling seamless integration with the host application.

// Authentication Container: Notifies storefront of login/logout
function AuthContainer() {
const handleLogin = (userData) => {
// Login logic...
// Notify storefront of authentication change
events.emit('authenticated', true);
};
const handleLogout = () => {
// Logout logic...
// Notify storefront of authentication change
events.emit('authenticated', false);
};
return <AuthForm onLogin={handleLogin} onLogout={handleLogout} />;
}
// Storefront can listen for authentication changes
// This would be in the host application
const authListener = events.on('authenticated', (isAuthenticated) => {
if (isAuthenticated) {
showUserMenu();
enableCheckout();
} else {
hideUserMenu();
disableCheckout();
}
}, { eager: true });

Best Practices

  1. Always unsubscribe from events when components unmount to prevent memory leaks
  2. Use scopes to organize events by feature or component
  3. Enable eager loading when you need immediate access to current state
  4. Use descriptive event names that clearly indicate what data they contain
  5. Handle null/undefined payloads gracefully in your event handlers
  6. Enable logging during development to debug event flow
  7. Keep event payloads lightweight to avoid performance issues
  8. Document your event contracts so other developers know what to expect