SE Wishlist Engine is Built for Shopify · 4,8★ uit 275+ reviewsGratis installeren

Events reference

Poison themeVersie 1.0.0Bijgewerkt op 7 okt. 2026

Op deze pagina

Poison announces every important storefront interaction as a DOM event, so apps, custom code and analytics can react without depending on the theme's markup. There are two layers:

  1. Poison events (cart:updated, variant:change, …): CustomEvents dispatched on document with a detail payload. They always fire after the page has been updated, so listeners see the fresh state.
  2. Shopify standard storefront events (shopify:cart:lines-update, shopify:page:view, …): the platform-wide events every app understands, dispatched from the element where the interaction happened. See Standard events.

This page is the reference for developers who build on the theme.


Listening

document.addEventListener('cart:updated', (event) => {
  const { cart, action } = event.detail;
  console.log(`Cart ${action}: ${cart.item_count} items`);
});

Every Poison event's detail also has timestamp (milliseconds) and source ("poison", or "standard-action" when the change came from Shopify.actions.updateCart).

Asking the cart UI to refresh

After your code changes the cart outside the theme (for example with fetch('/cart/add.js')), tell every cart UI on the page — drawer, header count, cart page, free-shipping goal — to re-render:

document.dispatchEvent(new CustomEvent('cart:refresh'));

Poison combines simultaneous refresh requests into one Section Rendering API request. Prefer Shopify.actions.updateCart(...) when you can: it updates the cart and the theme's UI in one call and emits the standard events for you.


Catalog

Event Fires when detail
cart:item-added Items were added successfully { items, cart, sourceElement, productId, variantId, quantity }
cart:item-removed A line was removed { lineKey, item, cart }
cart:item-updated A line's quantity changed { lineKey, previousQuantity, quantity, cart }
cart:updated Any cart change completed (after the specific event) { cart, action }
cart:refresh Fired by Poison after cart:updated with handled: true; listened for without it { handled?, sections? }
cart:error A cart request failed or the cart declined it { action, message, status, variantId? }
cart:note-updated The cart note was saved { note, cart }
cart:drawer-open / cart:drawer-close The cart drawer opened (from the header button, or after cart:item-added when the cart type is drawer) or closed { trigger }
cart:empty The cart became empty { cart }
variant:change The selected variant changed and the product section updated { productId, variant, previousVariant, sectionId, available }
variant:unavailable The selected option values match no variant (a sold-out variant fires variant:change with available: false) { productId, selectedOptions }
product:quantity-change A product form's quantity changed { productId, variantId, quantity }
product:media-change The product gallery's active media changed { productId, mediaId }
quick-add:open / quick-add:close The in-card options panel or quick add modal opened or closed { productHandle }
facets:updated A filter, sort or page change re-rendered a collection or the search results { url, sectionId, resultsCount }
search:predictive-results Predictive search results rendered { term, resultCount }
localization:change The country or language selector was submitted { country, language }
section:rendered Any section was re-rendered through the Section Rendering API: cart sections, a variant change, filters, predictive search, quick add; also when product recommendations, complementary products or recently viewed load { sectionId }; cart sections add html, recommendations productId and intent

variant and previousVariant on variant:change are { id, exists, available, title, sku, price, currency, options, selected_options, featured_media_id } (price in the currency's subunit, as Liquid outputs it). cart is the AJAX cart object (the shape returned by /cart.js). action on cart:updated is one of add, remove, update, note or standard-action.

Example payloads

// cart:item-added
{
  items: [{ id: 44011, variant_id: 44011, product_id: 8812, quantity: 1, title: 'Reflection slip dress - Lacquer red / S', … }],
  cart: { token: '…', item_count: 3, total_price: 84900, items: [ … ], … },
  sourceElement: <article class="po-card" …>,
  productId: 8812,
  variantId: 44011,
  quantity: 1,
  timestamp: 1791037200000,
  source: 'poison'
}

// cart:item-updated
{ lineKey: '44011:8a6b…', previousQuantity: 1, quantity: 2, cart: { … } }

// cart:error
{ action: 'add', message: 'All 3 Reflection slip dress - Lacquer red / S are in your cart.', status: 422, variantId: 44011 }

// variant:change
{ productId: 8812, variant: { id: 44012, title: 'Ink / M', available: true, price: 42000, … }, previousVariant: { … }, sectionId: 'template--main', available: true }

// facets:updated
{ url: '/collections/new-in?filter.v.availability=1&sort_by=price-ascending', sectionId: 'template--main-collection', resultsCount: 24 }

Order and loop prevention

Every cart change follows the same order:

  1. The cart request is sent with the cart sections bundled (Section Rendering API).
  2. The returned HTML is swapped into the page.
  3. The specific event fires: cart:item-added, cart:item-removed, cart:item-updated or cart:note-updated.
  4. cart:updated { cart, action }.
  5. cart:empty { cart }, when the cart became empty.
  6. cart:refresh { handled: true, sections }.

Rule for cart UI: re-render on cart:refresh only when event.detail?.handled !== true. Poison's own broadcast is already handled, so this never causes a second request:

document.addEventListener('cart:refresh', (event) => {
  if (event.detail?.handled === true) return;
  // fetch and re-render this component
});

An external cart:refresh (no detail) triggers one coalesced request for all cart sections; that render emits section:rendered, never another cart:refresh.


Shopify standard events

Poison dispatches Shopify's standard storefront events for changes the theme makes itself, using Shopify's library from https://cdn.shopify.com/storefront/standard-events.js:

Standard event Dispatched by Poison From
shopify:page:view Once per page, after DOMContentLoaded document
shopify:cart:lines-update Before every theme cart add, change or removal, with a promise that settles when the request finishes the product card, product section or cart element
shopify:cart:error When a cart request fails (not when the cart declines a change) the same element
shopify:cart:note-update As the cart note starts to save, with the note; its promise resolves with the cart document
shopify:product:view Once when the product page loads, and when the quick add modal shows a product (context: 'dialog'), from <po-view-event> (snippets/product-view-event.liquid): the product, its selected variant and options the product section
shopify:collection:view Once when a collection page loads, from <po-view-event> in the product grid: the collection's ID, handle and product count the product grid section
shopify:cart:view When the cart page loads and each time the cart drawer opens or re-renders while open (context: 'dialog'), from <po-view-event> (snippets/cart-view-event.liquid): the quantity, total, lines and discount codes the cart section
shopify:product:select As a variant change starts, with the product and the chosen options; its promise resolves with the variant (ID, title, availableForSale, price, options), or null when no variant matches <po-product-info> (product page and featured product)
shopify:collection:update, shopify:search:update As a filter, sort or page change starts, with the filters and sort key read from the new URL; the promise resolves with the new number of results <po-facets>

When an app calls Shopify.actions.updateCart, the action emits the standard cart events itself; Poison's configured handler then re-renders the cart sections and emits cart:updated, cart:empty and cart:refresh { handled: true }, and returns detail.handledBy: 'poison' so app listeners can skip their own re-render. Shopify.actions.openCart opens Poison's cart drawer, or the cart page when the cart type isn't drawer.


For theme developers

Poison's modules are ES modules behind an import map:

import { POISON_EVENTS, emit, on } from '@poison/events';
import { add, change, update, get } from '@poison/cart-api';

const stop = on(POISON_EVENTS.CART_UPDATED, ({ detail }) => console.log(detail.cart));
await add({ items: [{ id: 44011, quantity: 1 }] });
stop();

emit(name, detail, target = document) adds timestamp and source. on(name, handler) returns a function that removes the listener. Use the cart-api functions instead of calling the AJAX endpoints directly: they bundle the cart sections and fire the events above in order.

Stuur ons je wensen

Gebruik je een van onze apps? Het helpcentrum is het snelst.