Skip to content

Hooks

For AI assistants: what to know before writing SparkLayer SDK code
  • What it is: window.spark, added to the storefront by the SparkLayer Core Script (version 4.x). On Shopify the SparkLayer app embed loads it; elsewhere it's <script async src="https://sparkcdn.io/sparkjs/<SITE_ID>/<live|test>"></script> in the theme's <head>. The old cdn.sparklayer.io/spark.latest.js and spark.1.x.js files are legacy: never use them.
  • Options: Configure it with window.sparkOptions, set before the Core Script loads. Extend the object rather than replacing it: window.sparkOptions = { ...window.sparkOptions, … }, or the Site ID and other snippets' hooks are lost. Headless only: window.initSpark(options) is defined when window.sparkOptions isn't set.
  • When to call it: Only after SparkLayer is ready: inside the onReady(spark) hook, or after a promise resolved from it. Most methods are async; prices and the cart are only available to signed-in B2B customers (check await spark.isLoggedIn()).
  • Products and prices: Products are looked up by the platform's parent product ID (on Shopify, product.id) and variants by SKU: getPricingForVariant(parentProductId, sku), calculatePricingForVariant(parentProductId, sku, qty), getPackSizeForVariant(parentProductId, sku). For instant recalculation, fetch once with getVariant and call calculatePricingForVariantData(variant, qty).
  • Cart: updateCart({ products: [{ sku, adjustQuantity }] }) adds or removes relative to what's in the cart (also customAttributes: [{ key, value }], clearCart: true). It resolves to the results, or null after showing its own error message; it doesn't throw. getCart() reads the cart; openDrawer('cart') opens it.
  • Hooks: onCartUpdate(cart, results), onCartLoad(cart), onLogout(), preCartUpdateListener(input) (async: return the possibly changed input) and onCheckoutValidation(cart, cartUiState) (return [{ message }] to block checkout, or null). When adding a hook, call any existing one first.
  • Customising: Build around SparkLayer or customise its own components, including their HTML. Prefer documented options, hooks, CSS variables and custom slots where they fit; keep direct HTML changes targeted and re-check them after SparkLayer updates.
  • Docs: The reference: methods docs.sparklayer.io/developers/javascript-sdk/reference/methods.md, hooks docs.sparklayer.io/developers/javascript-sdk/reference/hooks.md, options docs.sparklayer.io/developers/javascript-sdk/reference/options.md, types docs.sparklayer.io/developers/javascript-sdk/reference/types.md (or the same types as a file: docs.sparklayer.io/developers/javascript-sdk/spark.d.ts). Setup and waiting for SparkLayer: docs.sparklayer.io/developers/javascript-sdk/setup.md. Use only what the reference documents.

Hooks are functions you set on window.sparkOptions. SparkLayer calls them at key points so you can run your own code. Each hook returns a promise, so define them as async functions. The optional hooks (onReady, onLoad, onLogout) do nothing unless you set them; the others have defaults that do nothing (or, for preCartUpdateListener, return the input unchanged).

Set hooks before the Core Script loads. A hook with the same name replaces the earlier one, so keep a reference to any hook that's already set and call it from yours:

Theme <head>, before the SparkLayer script
<script>
  (function () {
    var options = (window.sparkOptions = window.sparkOptions || {});
    var previous = options.onCartUpdate;

    options.onCartUpdate = async function (cart, results) {
      if (previous) await previous(cart, results);
      // Your code here
    };
  })();
</script>

The examples below use the same pattern. See Add your own options and hooks.

HookWhen it runs
onReadyWhen SparkLayer has initialised and the page's DOM has loaded.
onLoadOnce while SparkLayer initialises, before onReady.
onLogoutWhen the user logs out.
onCartLoadWhen the cart is loaded.
onCartUpdateEach time the cart is updated.
preCartUpdateListenerJust before the cart is updated, to change the update.
onCheckoutValidationEach time the cart loads or changes, to block checkout.

onReady()

Called when SparkLayer is ready: after it has initialised and the page's DOM has loaded. It runs whether or not the user is logged in. Start most of your code here.

Signature
onReady?(spark: Spark): Promise<void>;
NameTypeDescription
sparkSparkThe initialised Spark object.

Returns: Promise<void>

sparkOptions
const previousOnReady = window.sparkOptions?.onReady;
window.sparkOptions = {
  ...window.sparkOptions,
  onReady: async (spark) => {
    if (previousOnReady) await previousOnReady(spark);
    if (!(await spark.isLoggedIn())) return;
    // Safe to call spark methods here
  },
};

To wait for SparkLayer from a script that may run later, use window.sparkReady.

onLoad()

Called once while SparkLayer initialises, after translations have loaded and before onReady. It runs whether or not the user is logged in.

Signature
onLoad?(spark: Spark): Promise<void>;
NameTypeDescription
sparkSparkThe Spark object.

Returns: Promise<void>

sparkOptions
const previousOnLoad = window.sparkOptions?.onLoad;
window.sparkOptions = {
  ...window.sparkOptions,
  onLoad: async (spark) => {
    if (previousOnLoad) await previousOnLoad(spark);
    document.documentElement.dataset.sparkPlatform = spark.options.platform;
  },
};

onLogout()

Called when the user logs out. On a headless storefront, use it to log the customer out of your own app too (see Headless and React).

Signature
onLogout?(): Promise<void>;

Returns: Promise<void>

sparkOptions
const previousOnLogout = window.sparkOptions?.onLogout;
window.sparkOptions = {
  ...window.sparkOptions,
  onLogout: async () => {
    if (previousOnLogout) await previousOnLogout();
    sessionStorage.removeItem('b2bCampaign');
  },
};

onCartLoad()

Called when the cart is loaded. Use it to trigger analytics, for example.

Signature
onCartLoad(cart: Cart): Promise<void>;
NameTypeDescription
cartCartThe loaded cart.

Returns: Promise<void>

sparkOptions
const previousOnCartLoad = window.sparkOptions?.onCartLoad;
window.sparkOptions = {
  ...window.sparkOptions,
  onCartLoad: async (cart) => {
    if (previousOnCartLoad) await previousOnCartLoad(cart);
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({ event: 'b2b_cart_load', lines: cart.items.length, currency: cart.currencyCode });
  },
};

onCartUpdate()

Called when the cart is updated. Use it to trigger analytics or open the cart drawer, for example.

Signature
onCartUpdate(cart: Cart, results: CartResults[]): Promise<void>;
NameTypeDescription
cartCartThe updated cart.
resultsCartResults[]The result of each change in the update.

Returns: Promise<void>

sparkOptions
const previousOnCartUpdate = window.sparkOptions?.onCartUpdate;
window.sparkOptions = {
  ...window.sparkOptions,
  onCartUpdate: async (cart, results) => {
    if (previousOnCartUpdate) await previousOnCartUpdate(cart, results);
    window.spark.openDrawer('cart');
  },
};

For more, see Open the cart drawer and Send cart changes to your analytics.

preCartUpdateListener()

Called just before one of SparkLayer's add-to-cart buttons updates the cart. Use it to change the products before the update is sent: for example, to add custom attributes.

Signature
preCartUpdateListener(input: Partial<UpdateCart>): Promise<Partial<UpdateCart>>;
NameTypeDescription
inputPartial<UpdateCart>The cart update about to be sent. It has only the products being added, not the current cart.

Returns: Promise<Partial<UpdateCart>>: the cart update to send, changed or not. SparkLayer sends whatever you return.

sparkOptions
const previousPreCartUpdate = window.sparkOptions?.preCartUpdateListener;
window.sparkOptions = {
  ...window.sparkOptions,
  preCartUpdateListener: async (input) => {
    const update = previousPreCartUpdate ? await previousPreCartUpdate(input) : input;
    const length = document.querySelector('#shirt-length')?.value;
    if (!length || !update.products) return update;
    for (const product of update.products) {
      product.customAttributes = [...(product.customAttributes ?? []), { key: 'Shirt Length', value: length }];
    }
    return update;
  },
};

See Add attributes to SparkLayer's add to cart for more examples, including files.

onCheckoutValidation()

Checks whether the user can check out with their current cart. It runs each time the cart loads or changes, and receives the current checkout step, so your rules can vary by step. It must be asynchronous.

Signature
onCheckoutValidation(
  cart: Cart,
  cartUiState: CartUIState,
): Promise<{ message: string }[] | null>;
NameTypeDescription
cartCartThe current cart.
cartUiStateCartUIStateThe current checkout step.

Returns: Promise<{ message: string }[] | null>: an array of error objects, or null if there are no errors. SparkLayer shows each message to the customer.

sparkOptions
const previousOnCheckoutValidation = window.sparkOptions?.onCheckoutValidation;
window.sparkOptions = {
  ...window.sparkOptions,
  onCheckoutValidation: async (cart, cartUiState) => {
    const errors = (previousOnCheckoutValidation && (await previousOnCheckoutValidation(cart, cartUiState))) || [];
    const samples = cart.items
      .filter((item) => item.sku.startsWith('SAMPLE-'))
      .reduce((total, item) => total + item.quantity, 0);
    if (samples > 3) errors.push({ message: 'You can order up to 3 samples per order.' });
    return errors.length ? errors : null;
  },
};

See Add custom checkout validation.

Hook error codes

If onReady, onCartLoad or onCartUpdate throws an error when SparkLayer calls it, SparkLayer shows a system error toast with one of these codes and logs the error to the browser console. (SparkLayer doesn't wait for these hooks, so an async hook that rejects later shows no code: the browser logs it as an unhandled rejection.)

CodeHook that failed
OPTIONS:HOOK:ORonReady
OPTIONS:HOOK:OCLonCartLoad
OPTIONS:HOOK:OCUonCartUpdate

If customers report one of these codes, check that hook in your sparkOptions and fix the error shown in the console.

Next steps

Was this page helpful?

Last updated