Skip to main content

Call actions

Actions are functions on window.Shopify.actions that work on the cart. getCart reads it, updateCart changes lines, the note, and discount codes, and openCart shows it to the buyer. Every Liquid storefront includes all three, with nothing to install.

The storefront decides what each call does, so an app can add an item to the cart without knowing whether that cart is a drawer or a page. That difference stays on the storefront's side, and your calling code is the same whichever way it renders the cart.


Every call goes through Shopify.actions and returns a promise:

const { cart } = await Shopify.actions.getCart();

Actions are ready after DOMContentLoaded fires. Shopify loads the runtime as a module script, and browsers run those only after the HTML finishes parsing, so an earlier call finds Shopify.actions undefined and throws a TypeError.

A call that would otherwise run at page load goes inside a listener:

document.addEventListener('DOMContentLoaded', async () => {
const { cart } = await Shopify.actions.getCart();
});

Each action resolves differently. openCart resolves with nothing, getCart resolves with the cart, and updateCart resolves with the cart, adding userErrors and warnings when there are any.

The arrays exist because the cart can reject a change without the call failing. The promise rejects only when the action couldn't run at all, such as a network failure or a malformed payload.

A userError means the change didn't apply, so the example below stops there. A warning means it applied with an adjustment, like a line capped at the stock available, so the example shows the message and carries on:

const { cart, userErrors, warnings } = await Shopify.actions.updateCart({
lines: [{ merchandiseId: 'gid://shopify/ProductVariant/123', quantity: 1 }],
});

if (userErrors?.length) {
showMessage(userErrors[0].message);
return;
}

if (warnings?.length) {
showMessage(warnings[0].message);
}

updateCartCount(cart.totalQuantity);

required

The cart after the update, including lines, cost, and totalQuantity.

Anchor to userErrors
userErrors
[]

Present when the mutation was rejected. The cart stayed as it was for that input. Each entry has a message, and usually a code from CartErrorCode and the field that caused it.

Anchor to warnings
warnings
[]

Present when the mutation succeeded but returned non-blocking warnings. The cart did change. Each entry has a message, and usually a code from CartWarningCode.

Anchor to detail
detail
Record<string, unknown>

Custom data attached by the storefront's handler, used to tell one update path from another.

A storefront can replace what updateCart does with its own handler. That handler is expected to resolve with this same shape, so your code doesn't need to know whether it ran.


Anchor to Checking whether a storefront configured an actionChecking whether a storefront configured an action

A storefront that configures an action updates its UI in place. Without a configuration, the default refreshes the cart itself where it recognizes how the storefront renders it, and falls back to a full page reload where it doesn't. A reload throws away scroll position and anything the buyer had typed.

isDefault() returns true while an action is still running Shopify's default, so you can check before you call:

if (!Shopify.actions.updateCart.isDefault()) {
// The storefront has configured this action.
}

Anchor to Adding context to emitted eventsAdding context to emitted events

Storefront components react to cart events without knowing what caused them. An update from your app reaches them looking the same as a buyer clicking add to cart.

You can label the events your call emits with the event option, so a listener can tell your change from a buyer's. This detail is attached to the emitted events. A separate detail can come back on the result, set by the storefront's handler:

await Shopify.actions.updateCart(
{ lines: [{ merchandiseId: variantId, quantity: 1 }] },
{ event: { context: 'product', detail: { source: 's-product-form' } } },
);

Anchor to context
context
'product' | 'cart' | 'dialog' | 'standard-action'

Where the change came from. Sets the context field on the emitted shopify:cart:lines-update and shopify:cart:note-update events, and defaults to standard-action.

Anchor to detail
detail
Record<string, unknown>

Custom data attached to the emitted events, which listeners read from the event.


When a buyer changes a quantity twice in a row, the first result is out of date before it lands. You can cancel that request with an AbortSignal:

let controller;

async function setQuantity(lineId, quantity) {
controller?.abort();
controller = new AbortController();

return Shopify.actions.updateCart(
{ lines: [{ id: lineId, quantity }] },
{ signal: controller.signal },
);
}

An aborted call rejects, so catch that separately from a real failure. getCart takes a signal too, but concurrent reads share one request, so aborting yours ends your wait without cancelling anyone else's.



Was this page helpful?