JavaScript events
The theme implements Shopify's Standard Storefront Events and Actions: the cross-theme interface that apps, analytics tools, and AI agents use to read and drive the storefront. You integrate against the standard once. It works without forking theme files, parsing markup, or intercepting network requests. The same code keeps working on any other theme that implements the standard.
Two Shopify references define the contract:
Anything you find in the theme source that is not a shopify: event or a Shopify.actions call is internal wiring. It can change at any time, so do not depend on it.
Listening for events
Every event bubbles to document. Add a listener and read the event properties:
document.addEventListener('shopify:cart:lines-update', (event) => {
console.log(event.action, event.lines); // "add", "update", or "remove"
event.promise?.then(({ cart }) => {
console.log(cart.totalQuantity, cart.cost.totalAmount.amount);
});
});
Events that describe an operation in progress (the cart mutations, variant selection, and the collection or search refresh) carry a promise. It resolves once the operation commits and gives you the canonical result: the updated cart, the selected variant, or the new product count. Read the promise when you need the final state rather than the optimistic one.
Events the theme emits
| Event | Fires when |
|---|---|
shopify:page:view |
every page load |
shopify:product:view |
a product page loads, and each product card as it scrolls into view |
shopify:product:select |
the shopper changes a variant |
shopify:collection:view |
a collection page loads |
shopify:collection:update |
collection filters or sort change |
shopify:search:update |
search filters or sort change, and each set of search suggestions |
shopify:cart:view |
the cart drawer opens, or the cart page loads |
shopify:cart:lines-update |
a line is added, updated, or removed |
shopify:cart:note-update |
the cart note changes |
shopify:cart:discount-update |
a discount is applied or removed |
shopify:cart:error |
a cart operation fails |
Payloads follow the Shopify Storefront GraphQL API shape: camelCase fields, and prices as money objects with amount and currencyCode. The full payload for each event is documented in Shopify's standard events reference linked above.
Some of these fire more often than their names suggest. shopify:product:view fires once per product card as it scrolls into view, carrying a context of collection, search, or recommendation, so a collection page emits many of them. shopify:cart:view fires with a context of page on the cart page as well as when the drawer opens. Read the context field if you need to tell these surfaces apart.
shopify:search:update fires in two places: on the search results page when filters or sort change, and in the search overlay each time a set of suggestions is shown. One shopper typing a query therefore produces several. The overlay events carry the query and resolve their promise with the number of suggestions found, and they carry no product filters, because the page behind the overlay is unrelated to the query.
Driving the cart
Shopify injects a Shopify.actions object on every storefront. The theme configures it so your calls render in the theme's own drawer and header, with no page reload.
// Add to cart. Updates the theme's drawer and header in place, no reload.
const { cart, userErrors } = await Shopify.actions.updateCart({
lines: [{ merchandiseId: 'gid://shopify/ProductVariant/123', quantity: 1 }],
});
await Shopify.actions.openCart(); // opens the theme's cart drawer
const { cart: current } = await Shopify.actions.getCart(); // reads the current cart
updateCart also emits the matching shopify:cart: events for you, so code that calls it does not need to dispatch them too. Always check userErrors before you use cart.
Common examples
Track add to cart in analytics
document.addEventListener('shopify:cart:lines-update', (event) => {
if (event.action !== 'add') return;
event.promise?.then(({ cart }) => {
myAnalytics.addToCart(cart);
});
});
Update the cart from your own code
await Shopify.actions.updateCart({
lines: [{ merchandiseId: 'gid://shopify/ProductVariant/123', quantity: 1 }],
});
// The theme re-renders its cart drawer and header. No extra refresh call needed.
React when a shopper picks a variant
document.addEventListener('shopify:product:select', (event) => {
event.promise?.then(({ variant }) => {
if (variant) updateBadge(variant.availableForSale);
});
});
Pause background media when the cart drawer opens
document.addEventListener('shopify:cart:view', () => {
video.pause();
});