---
title: Dispatch events
description: >-
  Dispatch standard storefront events as DOM events when commerce interactions
  happen, so apps and agents can respond to them.
source_url:
  html: 'https://shopify.dev/docs/api/storefront-events-and-actions/events/dispatch'
  md: >-
    https://shopify.dev/docs/api/storefront-events-and-actions/events/dispatch.md
api_name: storefront-events-and-actions
---

# Dispatch events

You dispatch standard events so apps can respond to what buyers do on your storefront, without reading your markup or watching your network calls. An app listening for `shopify:cart:lines-update` gets the same payload from your theme as it does from any other.

These are ordinary DOM events. The standard events library gives you a class for each one, so you construct an event with its payload and dispatch it on the element where the interaction happened. It bubbles to `document`, and a listener can attach anywhere above it.

You add these calls next to the code that already handles the interaction. The cart events are the exception. When an app calls [`updateCart`](https://shopify.dev/docs/api/storefront-events-and-actions/actions/update-cart), the action [emits those itself](https://shopify.dev/docs/api/storefront-events-and-actions/actions/configure#auto-emitted-events), whether or not your storefront has configured it.

So don't dispatch a cart event after an `updateCart`, or listeners see the same change twice. Your own calls cover the changes your theme makes on its own.

***

## Loading the library

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`:

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

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

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

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

***

## Dispatching an event

Every event is a class. You construct it with `new ClassName(payload)` and dispatch it with `element.dispatchEvent()`:

```javascript
import { PageViewEvent } from '@shopify/standard-events';


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

`shopify:page:view` belongs inside a `DOMContentLoaded` listener, so app scripts have time to attach their listeners first.

Every other event dispatches from the most specific element that contains the interaction:

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

Because events bubble, a listener on `document` receives them wherever they come from, while dispatching on a specific element lets other listeners scope to part of the page. Each event's [reference page](https://shopify.dev/docs/api/storefront-events-and-actions/events) names its target.

***

## Dispatching view events from Liquid

[`shopify:product:view`](https://shopify.dev/docs/api/storefront-events-and-actions/events/product-view), [`shopify:collection:view`](https://shopify.dev/docs/api/storefront-events-and-actions/events/collection-view), and [`shopify:cart:view`](https://shopify.dev/docs/api/storefront-events-and-actions/events/cart-view) fire when a buyer sees something, so dispatching them means tracking element visibility.

The library ships a custom element that does the tracking for you. You register it once in your theme's JavaScript, under whatever tag name you want:

```javascript
import { createViewEventElement } from '@shopify/standard-events';


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

Then you wrap the content in that element. The [`standard_event_data`](https://shopify.dev/docs/api/liquid/filters/standard_event_data) filter builds the payload from a product, collection, or cart object:

```liquid
<s-view-event
  view-event-trigger="intersect"
  view-event-payload='{{ product | standard_event_data: "view", context: "collection" | escape }}'
>
  <!-- product card content -->
</s-view-event>
```

The filter's optional `context` argument records where the view happened. Which values it takes depends on the object, so check the [filter reference](https://shopify.dev/docs/api/liquid/filters/standard_event_data) before you pass one.

The `view-event-trigger` attribute controls when the element dispatches. It defaults to `connect`, which fires as soon as the element is added to the page. `intersect` fires when the element scrolls into view, `dialog` fires when an ancestor `<dialog>` opens, and `manual` waits for a `dispatchViewEvent()` call.

***

## Dispatching an operation that can fail

A cart update takes time and might not succeed, so its event carries a promise. You dispatch as the operation starts and settle the promise after you know how it went, which is what lets a listener show a loading state in between.

`createPromise()` on the event class returns that promise and its resolvers. The example below is the whole pattern end to end: dispatch, run your own cart code, then resolve or reject, and dispatch [`shopify:cart:error`](https://shopify.dev/docs/api/storefront-events-and-actions/events/cart-error) if the request failed.

```javascript
import { CartLinesUpdateEvent, CartErrorEvent } from '@shopify/standard-events';


const deferred = CartLinesUpdateEvent.createPromise();


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


try {
  const ajaxCart = await addToCart(variantId);


  deferred.resolve({
    cart: CartLinesUpdateEvent.createCartFromAjaxResponse(ajaxCart),
  });
} catch (error) {
  element.dispatchEvent(new CartErrorEvent({
    error: error.message,
    code: 'SERVICE_UNAVAILABLE',
  }));


  deferred.reject(error);
}
```

An unresolved promise leaves every listener waiting forever, which shows up as a spinner that never stops. Every path through your code needs to end in a resolve or a reject.

`addToCart()` is your theme's own cart request. If it uses the AJAX cart API, then `createCartFromAjaxResponse()` converts a `/cart.js` response into the cart shape the payload expects. If it calls the Storefront API directly, then build the cart from the GraphQL response instead.

### Which failures get an error event

When a request fails, you reject the promise and dispatch `shopify:cart:error`, as the catch block above does. The rejection reaches whoever called the operation, and the event reaches every other listener on the page.

When the cart declines a change, you resolve the promise instead. An unavailable variant or a discount code that doesn't apply comes back with `userErrors` describing the problem, and you don't dispatch an error event.

The `code` field takes a Storefront API cart error code, such as `INVALID` for a malformed input or `MAXIMUM_EXCEEDED` for a quantity above the item's maximum. See [`CartErrorCode`](https://shopify.dev/docs/api/storefront/latest/enums/CartErrorCode) for the full list.

***

## Validating payloads in development

It's easy to make a mistake and dispatch a payload with a field missing. The bug won't show up until an app tries to read that event.

`shopify theme dev` checks payloads for you. It serves a development build of the runtime that validates each one against the schema for its event, then prints what's wrong in the console:

```text
[Shopify Standard Events] Invalid payload for "shopify:cart:lines-update":
✖ Invalid input: expected string, received undefined
  → at lines[0].merchandiseId
```

Every message carries the `[Shopify Standard Events]` prefix, so you can filter the console down to them. The production runtime doesn't include these checks.

If you misspell an event name, the runtime has no schema to check it against. It throws an error instead of logging one.

If your event carries a `promise`, the payload gets checked twice. The fields are checked as you dispatch, and the resolved value is checked after the promise settles. That second message can appear well after the dispatch that caused it, so console order won't match the order you dispatched in.

You can also watch events as they fire and call actions by hand with the [`--standard-events-inspector`](https://shopify.dev/docs/api/shopify-cli/theme/theme-dev#flags-propertydetail-standardeventsinspector) flag, which adds a panel to your storefront while you develop.

**Note:**

These checks come from `shopify theme dev`. `shopify app dev` serves the production runtime, so an app or extension you run that way gets neither the checks nor the inspector.

***

## Next steps

[Events reference\
\
](https://shopify.dev/docs/api/storefront-events-and-actions/events)

[Look up the payload and dispatch target for every event.](https://shopify.dev/docs/api/storefront-events-and-actions/events)

[Standard events inspector\
\
](https://shopify.dev/docs/api/shopify-cli/theme/theme-dev#flags-propertydetail-standardeventsinspector)

[Watch events as they fire and call actions by hand while you develop.](https://shopify.dev/docs/api/shopify-cli/theme/theme-dev#flags-propertydetail-standardeventsinspector)

***
