Skip to main content

Standard storefront events

Standard storefront events are DOM events that themes dispatch when commerce interactions occur on the storefront, such as a product view, a cart update, or a collection filter change. Apps listen for these events with addEventListener to respond to storefront activity without parsing theme DOM structure or intercepting window.fetch.

Each event uses the shopify: namespace prefix (for example, shopify:cart:lines-update) and follows a category:action naming pattern. Event payloads follow the shape of the Storefront GraphQL API with camelCase property names, so listeners can read most of the data they need from the event directly.

Events dispatch from the most specific available contextual element and bubble to document. Typical targets include a product card, cart drawer, or collection container. Listeners can attach at any level in the tree.


The standard events library is hosted on the Shopify CDN. How you load it depends on whether your theme uses JavaScript modules.

If your theme uses JavaScript modules, as Horizon does, then register the library in an import map in theme.liquid:

<script type="importmap">
{ "imports": { "@theme/standard-events": "https://cdn.shopify.com/storefront/standard-events.js" } }
</script>

Type definitions are available at https://cdn.shopify.com/storefront/standard-events.d.ts.

If your theme uses plain <script> tags without modules, such as some Dawn-derived themes, then assign the library to a global instead:

<script type="module">
import * as SE from 'https://cdn.shopify.com/storefront/standard-events.js';
window.Shopify = window.Shopify || {};
window.Shopify.StandardEvents = SE;
</script>

Create events with new ClassName(payload) and dispatch them with element.dispatchEvent(). Dispatch page:view inside a DOMContentLoaded listener so that app scripts have time to attach their listeners:

import { PageViewEvent } from '@theme/standard-events';

document.addEventListener('DOMContentLoaded', () => {
document.dispatchEvent(new PageViewEvent({
page: {
template: 'product',
title: document.title,
url: window.location.href,
},
}));
});

Dispatch on the most specific container element:

  • Product events dispatch on the product card or section.
  • Cart events dispatch on the cart drawer or cart page section.
  • page:view dispatches on document.

Anchor to View events with the Liquid filterView events with the Liquid filter

For product:view, collection:view, and cart:view, use the standard_event_data Liquid filter together with the view event custom element. The filter generates the payload, and the custom element dispatches the event when the element scrolls into view.

Register the custom element once in your theme's JavaScript:

import { createViewEventElement } from '@theme/standard-events';

customElements.define('s-view-event', createViewEventElement());

Then wrap your content in Liquid:

<s-view-event view-event-payload='{{ product | standard_event_data: "view" | escape }}'>
<!-- product card content -->
</s-view-event>

You can pass a context to the filter: {{ product | standard_event_data: "view", context: "page" }}. If no context is provided and the element is inside a <dialog>, then the context is set to dialog automatically.


Some events represent async operations: cart:lines-update, cart:note-update, cart:discount-update, product:select, collection:update, and search:update. These events include a promise field. Dispatch the event before the async operation starts so listeners can show loading states or optimistic UI, then resolve the promise when the operation completes:

import { CartLinesUpdateEvent, CartErrorEvent } from '@theme/standard-events';

const deferred = CartLinesUpdateEvent.createPromise();

element.dispatchEvent(new CartLinesUpdateEvent({
action: 'add',
context: 'product',
lines: [{ merchandiseId: variantId, quantity: 1 }],
promise: deferred.promise,
}));

try {
const response = await fetch(window.Shopify.routes.root + 'cart/add.js', { method: 'POST', body, headers });
if (!response.ok) throw new Error('Add to cart failed');
const ajaxCart = await fetch(window.Shopify.routes.root + 'cart.js').then(r => r.json());
deferred.resolve({
cart: CartLinesUpdateEvent.createCartFromAjaxResponse(ajaxCart),
});
} catch (e) {
element.dispatchEvent(new CartErrorEvent({
error: e.message,
code: 'SERVICE_UNAVAILABLE',
}));
deferred.reject(e);
}

Always resolve or reject the promise. Unresolved promises cause listeners to hang.


Anchor to AJAX cart response converterAJAX cart response converter

Cart event classes have a static createCartFromAjaxResponse(ajaxCart) method that converts the AJAX API /cart.js response into the cart format that event payloads expect (a subset of the Storefront API Cart object). Use this method when your theme uses the AJAX cart API.

If your theme uses the Storefront API directly, then build the cart payload from the GraphQL response.


You can listen for any standard event on document:

document.addEventListener('shopify:cart:lines-update', (event) => {
console.log(event.action, event.lines);

event.promise?.then(({ cart }) => {
console.log(cart.cost.totalAmount.amount);
});
});

Follow these conventions so payloads validate against the dev build and stay consistent with the Storefront API:

IDs accept raw numbers, strings, or full GID strings. Event constructors normalize them.

Prices use the MoneyV2 format: { amount: "10.99", currencyCode: "USD" }. Use the money_amount Liquid filter to convert from cents.

Payload shapes follow the Storefront GraphQL API with camelCase property names.

The sections below are a reference for each event, grouped by category, with its name, payload shape, and recommended dispatch target.


Page events fire on navigation. Dispatch them on document.

Event name: shopify:page:view

Fires on every page load. Dispatch inside a DOMContentLoaded listener so that app scripts have time to attach their listeners.

new PageViewEvent({
page: {
template: "product",
title: "Classic T-Shirt",
url: "https://example.myshopify.com/products/classic-tee"
}
})

Dispatch target: document.


Product events fire as buyers browse and configure products. Dispatch them on the product card, section, or form.

Event name: shopify:product:view

Fires when a product becomes visible in the viewport. Use the view event element and Liquid filter rather than dispatching manually.

new ProductViewEvent({
context: "collection", // "page" | "collection" | "search" | "dialog" | "recommendation"
selectedOptions: [
{ name: "Size", value: "Medium" },
{ name: "Color", value: "Blue" }
],
product: {
id: 123456,
title: "Classic T-Shirt",
handle: "classic-tee",
selectedVariant: { // null if no variant is selected
id: 789,
title: "Medium / Blue",
availableForSale: true,
price: { amount: "29.99", currencyCode: "USD" },
selectedOptions: [
{ name: "Size", value: "Medium" },
{ name: "Color", value: "Blue" }
]
}
}
})

Dispatch target: product card or product section.

Event name: shopify:product:select

Fires when the buyer changes a variant selection.

new ProductSelectEvent({
selectedOptions: [
{ name: "Size", value: "Large" }
],
product: {
id: 123456,
title: "Classic T-Shirt",
handle: "classic-tee"
},
promise: deferred.promise // resolves to { variant: { id, title, availableForSale, price, selectedOptions } | null }
})

Dispatch target: product section or product form.


Cart events fire when the cart is viewed or mutated. Dispatch them on the cart drawer or cart page section. The events for mutations include a promise field, covered in Deferred promises.

Event name: shopify:cart:view

Fires when the cart becomes visible, either as a drawer or modal, or as a full page.

new CartViewEvent({
context: "dialog", // "page" | "dialog"
cart: { // null if no cart exists
id: 12345,
totalQuantity: 3,
cost: {
totalAmount: { amount: "89.97", currencyCode: "USD" }
},
lines: [
{
id: 1001,
quantity: 2,
cost: {
totalAmount: { amount: "59.98", currencyCode: "USD" }
}
}
],
discountCodes: [
{ code: "SAVE10", applicable: true }
]
}
})

Dispatch target: cart drawer, cart modal, or cart page section.

Event name: shopify:cart:lines-update

Fires when cart lines are added, updated, or removed.

new CartLinesUpdateEvent({
action: "add", // "add" | "update" | "remove"
context: "product", // "product" | "cart" | "dialog" | "standard-action"
lines: [
// For "add": { merchandiseId, quantity }. For "update" / "remove": { id, quantity }. Set quantity to 0 to remove a line.
{ merchandiseId: 789, quantity: 1 }
],
promise: deferred.promise // resolves to { cart }
})

Dispatch target: cart element for updates and removes; product element for adds from a product page.

The "standard-action" value is reserved for events auto-emitted by standard storefront actions when a configured action runs. Themes dispatching the event manually should use "product", "cart", or "dialog".

Event name: shopify:cart:note-update

Fires when the cart note changes.

new CartNoteUpdateEvent({
context: "cart", // "product" | "cart" | "dialog" | "standard-action"
note: "Please gift wrap",
promise: deferred.promise // resolves to { cart }
})

Dispatch target: cart element.

Anchor to Cart discount updateCart discount update

Event name: shopify:cart:discount-update

Fires when discount codes are applied or removed.

new CartDiscountUpdateEvent({
discountCodes: [
{ code: "SAVE10" }
],
promise: deferred.promise // resolves to { cart }
})

Dispatch target: cart element.

Event name: shopify:cart:error

Fires when a cart mutation fails.

new CartErrorEvent({
error: "Product is out of stock",
code: "INVALID"
})

Dispatch target: cart element, or document if no cart element is available.


Collection events fire when a collection page loads or its filters change. Dispatch them on the collection container element.

Event name: shopify:collection:view

Fires when a collection page loads.

new CollectionViewEvent({
collection: {
id: 5001, // can be null
handle: "summer-sale",
productsCount: 42
}
})

Dispatch target: collection container element.

Event name: shopify:collection:update

Fires when collection filters or sort order change. Use CollectionUpdateEvent.parseProductFilters(urlSearchParams) to build the productFilters array from the current URL.

new CollectionUpdateEvent({
collection: {
id: 5001, // can be null
handle: "summer-sale",
productsCount: 18
},
productFilters: [ ... ], // optional
sortKey: "PRICE", // optional
promise: deferred.promise // resolves to { productsCount }
})

Dispatch target: collection container element.


Search events fire when search filters or sort order change. Dispatch them on the search container element.

Event name: shopify:search:update

Fires when search filters or sort order change. Use SearchUpdateEvent.parseProductFilters(urlSearchParams) to build the productFilters array from the current URL.

new SearchUpdateEvent({
search: {
query: "blue shirt",
productFilters: [ ... ], // optional
sortKey: "RELEVANCE" // optional
},
promise: deferred.promise // resolves to { totalCount }
})

Dispatch target: search container element.


Was this page helpful?