Events
Drop-in components implement an event-driven architecture that uses the @dropins/tools/event-bus.js module to facilitate communication between components. This event system enables drop-ins to respond to application state changes, maintain loose coupling between components, and keep their state synchronized across your storefront.
Event system architecture
The system uses a publish-subscribe pattern where components can:
- Subscribe to specific events using
events.on() - Emit events using
events.emit() - Unsubscribe using
subscription.off()
This pattern allows drop-ins to communicate without having direct dependencies on each other, making your storefront more modular and maintainable.
Common event patterns
Drop-ins use consistent naming conventions for their events:
dropin/initialized: Fires when a drop-in completes initializationdropin/updated: Fires when a drop-in’s state changesdropin/data: Provides the current data statedropin/reset: Fires when a drop-in’s state is resetdropin/error: Fires when an error occurs
Event directions
Events can flow in different directions:
- Emits: The drop-in publishes this event for others to consume
- Listens: The drop-in subscribes to this event from external sources
- Emits and listens: The drop-in both publishes and subscribes to this event (bidirectional)
Event subscription
Components subscribe to events to listen for and respond to the changes elsewhere in the application.
Subscription syntax
To subscribe to an event, provide:
- The event name (as a string)
- An event handler callback function that receives the payload
- Optional configuration parameters
const subscription = events.on('event-name', handler, options);Subscription options
Event subscriptions support an optional configuration parameter:
eager: true: The handler executes immediately if the event has been emitted previouslyeager: false(default): The handler only responds to future emissions of the event
See Best Practices for detailed guidance on using eager mode effectively.
Example: Subscribing to an event
Listen to an initialization event:
import { events } from '@dropins/tools/event-bus.js';
// Subscribe to the eventconst subscription = events.on('cart/initialized', (data) => { console.log('Cart initialized with data:', data); // Handle the cart data updateUI(data);});
// Later, unsubscribe when no longer neededsubscription.off();Event emission
Components emit events to share information with other components, drop-ins, or external systems.
Emission syntax
To emit an event, provide:
- The event name (as a string)
- The payload containing the data to share
events.emit('event-name', payload);Example: Emitting an event
Emit an event when state changes:
import { events } from '@dropins/tools/event-bus.js';
function updateCartQuantity(itemId, quantity) { // Update the cart const updatedCart = performCartUpdate(itemId, quantity);
// Notify other components about the change events.emit('cart/updated', updatedCart);}Common events reference
Drop-ins use common events for cross-component communication, authentication management, localization, and error handling.
| Event | Category | Used By | Description |
|---|---|---|---|
| authenticated | Authentication | Most B2C & B2B drop-ins | Authentication state changes |
| error | Error Handling | Most drop-ins | Error notifications |
| locale | Localization | All drop-ins | Language/locale changes |
Best practices
Use type-safe event names
Import event types when available to ensure you’re using the correct event names:
import type { Events } from '@adobe-commerce/event-bus';
// TypeScript will validate the event nameevents.on('cart/initialized', (data) => { // ...});Use eager mode wisely
Set eager: true when you need the current state immediately:
// Good: Getting initial state on component mountevents.on('cart/data', (data) => { initializeComponent(data);}, { eager: true });
// Good: Only responding to future changesevents.on('cart/updated', (data) => { updateComponent(data);}, { eager: false });Keep handlers focused
Event handlers should be small and focused on a single responsibility:
// Good: Focused handlerevents.on('cart/updated', (cart) => { updateCartBadge(cart.itemCount);});
// Avoid: Handler doing too muchevents.on('cart/updated', (cart) => { updateCartBadge(cart.itemCount); updateMiniCart(cart); recalculateTotals(cart); logAnalytics(cart); // Too many responsibilities});Use state management helpers
Use events.lastPayload('<event>') to retrieve the most recent state without waiting for the next event:
// Get current authentication stateconst isAuthenticated = events.lastPayload('authenticated');if (isAuthenticated) { console.log('User is authenticated');}
// Get current localeconst currentLocale = events.lastPayload('locale');console.log('Current locale:', currentLocale);Handle errors gracefully
Always include error listeners in production applications to gracefully handle failures and provide helpful feedback to users.
Event sources: External vs. Internal
Events can originate from different sources in your storefront:
External events are fired by:
- Your storefront application code (authentication, locale changes)
- Other drop-ins (cart updates affecting checkout)
- Third-party integrations (payment processors, analytics)
Internal events are fired by:
- Components within the same drop-in (checkout steps communicating with each other)
- Drop-in initialization and state management
Understanding whether an event is external or internal helps you determine:
- Where to emit the event in your custom code
- Which events you need to handle from your storefront
- How drop-ins coordinate internally vs. with the broader application
The following diagram illustrates this using the Checkout drop-in as an example:
The Checkout drop-in:
- Listens to external events:
authenticated,cart/initialized,cart/updated,cart/merged,cart/reset,cart/data,locale - Uses internal events:
checkout/initialized,checkout/updated,shipping/estimate(for coordinating between its own containers)
Event declaration
Events are strongly typed using TypeScript declaration merging to provide type safety and autocomplete support. Each drop-in declares its events by extending the Events interface from the event bus.
Basic declaration
Here’s a simplified example of how events are declared:
declare module '@adobe-commerce/event-bus' { interface Events { 'dropin/initialized': DataModel | null; 'dropin/updated': DataModel | null; 'dropin/data': DataModel; authenticated: boolean; locale: string; error: { source: string; type: string; error: Error }; }}Complete declaration example
In practice, drop-ins declare their events with imports and type extensions. Here’s a more comprehensive example from the Checkout drop-in:
import { Cart as CheckoutData, ShippingEstimate, ValuesModel,} from '@/checkout/data/models';
import { CartModel } from '@/checkout/types/cart';
declare module '@adobe-commerce/event-bus' { interface Events { 'cart/initialized': CartModel | null; 'cart/updated': CartModel | null; 'cart/reset': void; 'cart/merged': { oldCartItems: any[] }; 'checkout/initialized': CheckoutData | null; 'checkout/updated': CheckoutData | null; 'checkout/values': ValuesModel; 'shipping/estimate': ShippingEstimate; authenticated: boolean; error: { source: string; type: string; error: Error }; }
interface Cart extends CartModel {}}This pattern allows TypeScript to provide autocomplete and type checking for both event names and their payloads throughout your application.
Next steps
- Review the Common events reference for detailed documentation on shared events
- Review the Event Bus API Reference for detailed API methods and code examples
- Check individual drop-in event pages for component-specific events
- Try drop-in tutorials for practical event usage examples