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 oldcdn.sparklayer.io/spark.latest.jsandspark.1.x.jsfiles 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 whenwindow.sparkOptionsisn'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 (checkawait 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 withgetVariantand callcalculatePricingForVariantData(variant, qty). - Cart:
updateCart({ products: [{ sku, adjustQuantity }] })adds or removes relative to what's in the cart (alsocustomAttributes: [{ key, value }],clearCart: true). It resolves to the results, ornullafter 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) andonCheckoutValidation(cart, cartUiState)(return[{ message }]to block checkout, ornull). 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:
<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.
| Hook | When it runs |
|---|---|
onReady | When SparkLayer has initialised and the page's DOM has loaded. |
onLoad | Once while SparkLayer initialises, before onReady. |
onLogout | When the user logs out. |
onCartLoad | When the cart is loaded. |
onCartUpdate | Each time the cart is updated. |
preCartUpdateListener | Just before the cart is updated, to change the update. |
onCheckoutValidation | Each 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.
onReady?(spark: Spark): Promise<void>;| Name | Type | Description |
|---|---|---|
spark | Spark | The initialised Spark object. |
Returns: Promise<void>
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.
onLoad?(spark: Spark): Promise<void>;| Name | Type | Description |
|---|---|---|
spark | Spark | The Spark object. |
Returns: Promise<void>
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).
onLogout?(): Promise<void>;Returns: Promise<void>
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.
onCartLoad(cart: Cart): Promise<void>;| Name | Type | Description |
|---|---|---|
cart | Cart | The loaded cart. |
Returns: Promise<void>
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.
onCartUpdate(cart: Cart, results: CartResults[]): Promise<void>;| Name | Type | Description |
|---|---|---|
cart | Cart | The updated cart. |
results | CartResults[] | The result of each change in the update. |
Returns: Promise<void>
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.
preCartUpdateListener(input: Partial<UpdateCart>): Promise<Partial<UpdateCart>>;| Name | Type | Description |
|---|---|---|
input | Partial<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.
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.
onCheckoutValidation(
cart: Cart,
cartUiState: CartUIState,
): Promise<{ message: string }[] | null>;| Name | Type | Description |
|---|---|---|
cart | Cart | The current cart. |
cartUiState | CartUIState | The 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.
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.)
| Code | Hook that failed |
|---|---|
OPTIONS:HOOK:OR | onReady |
OPTIONS:HOOK:OCL | onCartLoad |
OPTIONS:HOOK:OCU | onCartUpdate |
If customers report one of these codes, check that hook in your sparkOptions and fix the error shown in the console.
Next steps
Last updated