Global JavaScript API

Reference for the namespaced runtime object the Upsell theme extension attaches to window, its bootstrap helpers, and the onProductAddToCart callback.

When the Upsell theme app extension loads, it attaches a namespaced runtime object to the browser window. This object holds the campaign data that powers each widget, the extension's caches, and the callbacks your theme code can read or override. This page documents the global object and helpers you can rely on from your storefront.

These globals live on window, so you can inspect them directly from your browser's developer console on any page where an Upsell widget is rendered.

Overview

The extension attaches a single namespaced runtime object to window, keyed by the app handle (the slugified app name). That object holds the campaign data, product caches, and callbacks the widgets use at runtime. On live storefronts the widgets receive their campaign data through the bootstrap() params sourced from the Liquid _data JSON island — not from any freestanding data globals. The namespace object section below documents its shape.

The namespace object is populated by the extension as it initializes. Read it after the widget scripts have loaded — if you access it too early, it may be undefined. Because window is typed loosely, always use optional chaining (?.) before relying on any property.

You may see window.unlock and window.prePurchase while developing against the app's local dev harness — these are mock data used only in that harness. They are not present on live storefronts, so don't build against them.

The window namespace object

The extension also keeps its own runtime state under a namespaced key on window. The key is the app handle — the slugified app name — and the object behind it carries the app's runtime data and callbacks.

Shape

The namespace object can hold (among other things):

interface Extension {
  getAssetUrl?: (assetName: string) => string;
  libs?: Record<string, { bootstrap: (node?: HTMLElement) => void }>;
  cart: any;
  prePurchaseProducts: any;
  cachedProducts: Map<number, any>;
  cachedStorefrontProducts: Record<string, any>;
  cachedStorefrontRecommendations: Record<string, any>;
  appSettings: {
    appName: string;
    appHandle: string;
  };
  storeFrontAccessToken: {
    id: string;
    accessToken: string;
  };
}

bootstrap

Each registered widget library exposes a bootstrap function under libs. Calling it mounts the widget, optionally into a specific DOM node:

bootstrap: (node?: HTMLElement) => void;

You can re-mount a library by calling its bootstrap from the namespace:

// Re-bootstrap the "unlock" library
window[appHandle]?.libs?.unlock?.bootstrap?.();

onProductAddToCart

The cart utilities call an onProductAddToCart callback on the namespace object — if you have defined one — after a product is added to, changed in, updated in, or cleared from the cart. Define it to run your own logic when the cart changes through the extension:

window[appHandle].onProductAddToCart = async (lineItem) => {
  // Your logic here — e.g. refresh a custom cart drawer
  console.log('Product added or cart changed', lineItem);
};

When this callback exists, the extension awaits it instead of falling back to a full page reload after cart updates and clears.

window is typed loosely ([key: string]: any), so these properties are not guaranteed to exist on every page. Always use optional chaining (?.) and check for a value before relying on it.

Cart events

In addition to these globals, the extension dispatches DOM events on cart changes (for example a refresh event with the latest cart JSON). If you need to react to cart updates, listen for those events rather than polling the globals.

On this page