Theme events

Theme events serve as a communication mechanism between theme modules, or between theme modules and apps. Using standard events constructed via the event constructor and APIs provided by the event bus, you can trigger or receive theme events applicable to specific scenarios within theme modules or apps.

Event bus

The event bus acts as the central hub for information transmission. You can use the event bus to publish or subscribe to the theme events. The bus is responsible for notifying event subscribers, facilitating effective decoupling of modules, and enhancing system maintainability and scalability.

A global event bus is injected into the window object under the standard theme, accessible through window.themeEventCenter.

APIs

The event bus provides several APIs for easily publishing and subscribing to events.

dispatch

This API triggers a specific event.

Syntax:.dispatch(event: ThemeEvent): void

Properties
PropertyTypeRequired or notDescription
eventThemeEventYesThe object instance of the theme event to be triggered. You can create a custom event object instance or a standard event object instance.
Returns

None

Example

The following example shows triggering a Product viewed event through the event bus.

window.themeEventCenter.dispatch(
new ThemeEvent('product:viewed', {
detail: {
productId: '16069825781843611825520845',
productHandle: 'Shirt',
variantId: '18066825256105052042503900',
price: 9999,
currency: 'USD'
}
})
);

addListener

This API listens to a specific event.

Syntax: .addListener(eventName: string, callback: (event: Event) => void): EventListener

Properties
PropertyTypeRequired or notDescription
eventNamestringYesThe name of the theme event to listen for. It can be a custom event name or a standard event name.
callbackfuncYesA function that executes when the event is triggered. It usually accepts the event parameter, allowing access to event object data through event.detail.
For the specific data structure of event.detail, refer to Create an event object.
Returns

An EventListener object contains the following fields:

PropertyTypeDescription
eventNamestringThe name of the theme event being listened to.
removefuncThe method to remove the event listener.
Example

The following example shows listening to a Product viewed event through the event bus.

window.themeEventCenter.addListener('product:viewed', (event) => {
console.log(event.id);
console.log(event.target);
console.log(event.detail.productId);
});

getCurrentDetail

This API retrieves the detail data from the most recent trigger of a specific event.

Syntax: .getCurrentDetail(eventName: string): Record<string, any> | undefined

Properties
PropertyTypeRequired or notDescription
eventNamestringYesThe name of the theme event. It can be a custom event name or a standard event name.
Returns

The detail data of the corresponding theme event. For the specific data structure of event.detail, refer to Create an event object.

Example

The following example shows getting product data after the Product viewed event is triggered through the event bus.

const viewProduct = window.themeEventCenter.getCurrentDetail('product:viewed');
console.log(viewProduct?.productId); // Outputs the product ID

Event constructor

The event constructor is a tool for creating event objects, defining event types, carrying data, and related properties. You can flexibly configure events, assigning them specific behaviors and characteristics for different scenarios, facilitating triggering and transmission.

Standard themes inject an event constructor into the window object, accessible via window.ThemeEvent.

Create an event object

You can use new ThemeEvent to create an event object.

Syntax: new ThemeEvent(eventName: string, eventInitDict: EventInit)

Properties

Property

Type

Required or not

Description

eventName

string

yes

The name of the theme event.

eventInitDict

EventInit

Yes

The initialization data of the event. EventInit includes the following properties:

  • id (optional): Explicitly declare a unique identifier for the event, which will be passed to the event listener. If not provided, a unique identifier will be automatically generated.
  • target (optional): Explicitly declare the source DOM node that triggers the event.
  • detail (required): The relevant data carried by the event, which the event listener can access through Event.detail.

Standard data example:

interface EventInit {   
id?: string;
target?: Element;
detail: Record<string, any>
}

Returns

The event object ThemeEvent.

Example

The following example shows creating a Product viewed event.

new ThemeEvent('product:view', {
detail: {
...
}
});

Standard events

The event constructor supports the following types of standard events:

Product viewed

  • Event name: product:viewed
  • Trigger timing: This event is triggered when a customer enters a product detail page, opens a quick add-to-cart popup, or a single product component is displayed.
Standard data
interface Detail {
productId: string; // Product ID
productHandle: string; // Product handle
variantId?: string; // Product SKU ID. If the theme configuration does not have a default SKU selected, this will return an empty value.
price?: number; // Product SKU price. If the theme configuration does not have a default SKU selected, this will return an empty value.
currency: string; // Settlement currency
}
Example:
// Triggering the event
window.themeEventCenter.dispatch(
new ThemeEvent('product:viewed', {
detail: {
productId: '16069825781843611825520845',
productHandle: 'Shirt',
variantId: '18066825256105052042503900',
price: 9999,
currency: 'USD'
}
})
);
// Listening for the event
window.themeEventCenter.addListener('product:viewed', (event) => {
console.log(event.detail.productId); // Outputs the product ID
});

Product SKU changed

  • Event name: variant:changed
  • Trigger timing: Triggered when switching SKUs on the product detail page, quick add-to-cart popup, or single product component.
Standard data
interface Detail {
productId: string; // Product ID
productHandle: string; // Product handle
variantId: string; // Product SKU ID
quantity: number; // Product SKU quantity
price: number; // Product SKU price
currency: string; // Settlement currency
}
Example
// Triggering the event
window.themeEventCenter.dispatch(
new ThemeEvent('variant:changed', {
detail: {
productId: '16069825781843611825520845',
productHandle: 'Shirt',
variantId: '18066825256105052042503900',
quantity: 1,
price: 9999,
currency: 'USD'
}
})
);
// Listening for the event
window.themeEventCenter.addListener('variant:changed', (event) => {
console.log(event.detail.variantId); // Outputs product SKU ID
});

Product SKU added to the cart

  • Event Name: variant:added
  • Trigger timing: Triggered when adding a product to the cart.
Standard data
interface Detail {
lineItemKey: string; // Cart item identifier
productId: string; // Product ID
productHandle: string; // Product handle
variantId: string; // Product SKU ID
quantity: number; // Product SKU quantity
price: number; // Product SKU price. If the theme configuration does not have a default SKU selected, this will return an empty value.
currency: string; // Settlement currency
}
Example
// Triggering the event
window.themeEventCenter.dispatch(
new ThemeEvent('variant:added', {
detail: {
productId: '16069825781843611825520845',
productHandle: 'Shirt',
variantId: '18066825256105052042503900',
quantity: 1,
price: 9999,
currency: 'USD',
lineItemKey: '18064578594821059446022470:430512ac683d4903983eeee3c168ade8'
}
})
);
// Listening for the event
window.themeEventCenter.addListener('variant:added', (event) => {
// Outputs product SKU ID and quantity
console.log(event.detail.variantId, event.detail.quantity);
});

Cart opened

  • Event name: cart:opened
  • Trigger timing: Triggered when the cart is opened.
Standard data
interface Detail {
type: 'notification' | 'drawer'; // Cart type
}
Example
// Triggering the event
window.themeEventCenter.dispatch(
new ThemeEvent('cart:opened', {
detail: {
type: 'drawer'
}
})
);
// Listening for the event
window.themeEventCenter.addListener('cart:opened', (event) => {
const { detail } = event;
console.log('Cart opened', detail.type);
});

Cart closed

  • Event name: cart:closed
  • Trigger timing: Triggered when the cart is closed.
Standard data
interface Detail {
type: 'notification' | 'drawer'; // Cart type
}
Example
// Triggering the event
window.themeEventCenter.dispatch(
new ThemeEvent('cart:closed', {
detail: {
type: 'drawer'
}
})
);
// Listening for the event
window.themeEventCenter.addListener('cart:closed', (event) => {
const { detail } = event;
console.log('Cart closed', detail.type);
});

Open the cart

  • Event name: cart:open
  • Trigger timing: Triggered when the action to open the cart is initiated. The caller does not need to concern themselves with theme configurations or implementations, as these are handled by the theme itself.
Standard data
interface Detail {
refresh?: boolean; // Whether to refresh the cart contents, defaults to false
}
Example
// The "open cart" event is triggered
window.themeEventCenter.dispatch(
new ThemeEvent('cart:open', {
detail: {
refresh: true // Refresh the cart
}
})
);
// Listens for the "open cart" event and implements specific business logic
window.themeEventCenter.addListener('cart:open', (event) => {
// Retrieve the passed parameters
const { refresh } = event.detail;

// Retrieve data of the recently added product
const addedVariantDetail = themeEventCenter.getCurrentDetail('variant:added');
// Outputs the product ID
console.log(addedVariantDetail.productId);
});

Open the quick add-to-cart popup

  • Event name: quick-add:open
  • Trigger timing: Triggered when the action to open the add-to-cart popup is initiated. This is typically required during product addition scenarios.
Standard data
interface Detail {
productHandle: string; // Product handle
}
Example
// The "open quick add popup" event is triggered
window.themeEventCenter.dispatch(
new ThemeEvent('quick-add:open', {
detail: {
productHandle: 'Shirt'
}
})
);

Note: The logic for listening for the Open the quick add-to-cart popup event is already built in by the official theme. You do not need to implement it yourself.

Was this article helpful to you?