Skip to content
Commerce Drop-Ins

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.

The system uses a publish-subscribe pattern where components can:

  1. Subscribe to specific events using events.on()
  2. Emit events using events.emit()
  3. 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.

Loading diagram...
Drop-ins communicate through the central event bus without direct dependencies on each other. Custom storefront code (pink) can also emit events like locale and authenticated that drop-ins listen to.

Drop-ins use consistent naming conventions for their events:

  • dropin/initialized: Fires when a drop-in completes initialization
  • dropin/updated: Fires when a drop-in’s state changes
  • dropin/data: Provides the current data state
  • dropin/reset: Fires when a drop-in’s state is reset
  • dropin/error: Fires when an error occurs

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)
Loading diagram...
Three types of event flow: emits only (blue), listens only (indigo), and bidirectional (purple).

Components subscribe to events to listen for and respond to the changes elsewhere in the application.

To subscribe to an event, provide:

  1. The event name (as a string)
  2. An event handler callback function that receives the payload
  3. Optional configuration parameters
const subscription = events.on('event-name', handler, options);

Event subscriptions support an optional configuration parameter:

  • eager: true: The handler executes immediately if the event has been emitted previously
  • eager: false (default): The handler only responds to future emissions of the event

See Best Practices for detailed guidance on using eager mode effectively.

Listen to an initialization event:

import { events } from '@dropins/tools/event-bus.js';
// Subscribe to the event
const subscription = events.on('cart/initialized', (data) => {
console.log('Cart initialized with data:', data);
// Handle the cart data
updateUI(data);
});
// Later, unsubscribe when no longer needed
subscription.off();

Components emit events to share information with other components, drop-ins, or external systems.

To emit an event, provide:

  1. The event name (as a string)
  2. The payload containing the data to share
events.emit('event-name', payload);

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);
}

These three events are shared across multiple drop-ins. Your storefront code emits authenticated and locale; drop-ins emit error for your code to handle.

EventCategoryUsed ByDescription
authenticatedAuthenticationMost B2C & B2B drop-insAuthentication state changes
errorError HandlingMost drop-insError notifications
localeLocalizationAll drop-insLanguage/locale changes

Category: Authentication Direction: Emitted by an external source, listened to by drop-ins Used By: Cart, Checkout, Order, User Account, User Auth, Wishlist, and most B2B drop-ins

Fires when the user’s authentication state changes (login, logout, token refresh, session expiration). Drop-ins listen to this event to update their internal state and UI.

Emit this event from your storefront when:

  • An authentication token is refreshed
  • The authentication state is restored (for example, page refresh with active session)
  • A session expires
  • A user logs out
  • A user successfully logs in
boolean

true = user is authenticated. false = user is not authenticated or has logged out.

import { events } from '@dropins/tools/event-bus.js';
// User logged in
events.emit('authenticated', true);
// User logged out
events.emit('authenticated', false);
import { events } from '@dropins/tools/event-bus.js';
const authListener = events.on('authenticated', (isAuthenticated) => {
if (isAuthenticated) {
// Update UI, load user-specific data, etc.
} else {
// Clear user data, redirect to login, etc.
}
});
// Stop listening when no longer needed
authListener.off();

Category: Error Handling Direction: Emitted by drop-ins, external code listens Used By: Most drop-ins for error reporting

Fires when a drop-in encounters an error (API failure, validation error, network timeout). Listen to this event to display error messages, log errors, or trigger error recovery logic.

Drop-ins emit this event when:

  • API requests fail
  • Critical operations fail
  • Network errors occur
  • Validation fails
{
message: string;
code?: string;
details?: any;
source?: string;
}
import { events } from '@dropins/tools/event-bus.js';
const errorListener = events.on('error', (error) => {
console.error('Drop-in error:', error.message);
showErrorNotification(error.message);
if (error.code === 'AUTH_EXPIRED') {
redirectToLogin();
}
});
errorListener.off();

Category: Localization Direction: Emitted by external source, listened to by drop-ins Used By: All drop-ins with internationalization support

Fires when the application’s language or locale changes. Drop-ins listen to update their text content, date formatting, currency display, and other locale-specific elements.

Emit this event from your storefront when:

  • A user selects a different language
  • The application detects and applies a locale based on user preferences
  • The locale is programmatically changed
string

The locale string should follow standard format (for example, en-US, fr-FR, de-DE).

import { events } from '@dropins/tools/event-bus.js';
// User selects a new language
events.emit('locale', 'fr-FR');
// Or based on browser detection
const userLocale = navigator.language || 'en-US';
events.emit('locale', userLocale);
import { events } from '@dropins/tools/event-bus.js';
const localeListener = events.on('locale', (newLocale) => {
updateTranslations(newLocale);
});
localeListener.off();

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 name
events.on('cart/initialized', (data) => {
// ...
});

Set eager: true when you need the current state immediately:

// Good: Getting initial state on component mount
events.on('cart/data', (data) => {
initializeComponent(data);
}, { eager: true });
// Good: Only responding to future changes
events.on('cart/updated', (data) => {
updateComponent(data);
}, { eager: false });

Event handlers should be small and focused on a single responsibility:

// Good: Focused handler
events.on('cart/updated', (cart) => {
updateCartBadge(cart.itemCount);
});
// Avoid: Handler doing too much
events.on('cart/updated', (cart) => {
updateCartBadge(cart.itemCount);
updateMiniCart(cart);
recalculateTotals(cart);
logAnalytics(cart);
// Too many responsibilities
});

Use events.lastPayload('<event>') to retrieve the most recent state without waiting for the next event:

// Get current authentication state
const isAuthenticated = events.lastPayload('authenticated');
if (isAuthenticated) {
console.log('User is authenticated');
}
// Get current locale
const currentLocale = events.lastPayload('locale');
console.log('Current locale:', currentLocale);

Always include error listeners in production applications to gracefully handle failures and provide helpful feedback to users.


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:

Loading diagram...
External events (thin dashed arrows) flow from outside sources through the Event Bus to the drop-in. Internal events (thick solid arrows) coordinate between containers within the same drop-in.

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)

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.

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 };
}
}

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.


  • 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