Storefront SDK (window.Hype)
Updated Oct 1, 20265 min read
On this page (8)
Hype Discounts exposes its storefront runtime on window.Hype. Use it to read the shopper's active campaigns and cart, apply discount codes, format money in the shop's currency, and react to campaign events from your own theme code.
What loads it
The Hype: Discounts app embed loads everything. Turn it on at Online Store → Themes → Customize → App embeds → Hype: Discounts. The embed renders a module script that:
- Inlines two app metafields as JSON:
hype.ui_blocks_schemaandhype.pricing_schema. - Imports
hype.jsandhype-ui.jsfrom the app's assets. - Constructs the SDK and assigns these globals:
| Global | What it holds |
|---|---|
window.Hype | The SDK instance described on this page |
window.HypeUI | The controller classes behind the campaign elements — see UI SDK (window.HypeUI) |
window.HypeUIBlocksSchema | Block settings keyed by campaign ID |
window.HypeSwatchesConfig | The shop's variant swatch configuration |
window.HypeProductContext | { handle, collectionIds }, on product pages only |
The embed's Enable debug logs, API Proxy link (default /apps/hype) and Hide Decimals for Whole Numbers settings feed the SDK's configuration. Its Custom JS field runs on every page and is the simplest place to put the snippets below.
Waiting for the SDK
window.Hype exists as soon as the embed's module runs, but campaigns arrive later, over the app proxy. Check for data first, then fall back to the event:
const start = () => { console.log(window.Hype.campaigns)}if (window.Hype?.campaigns) { start()} else { document.addEventListener('hype:campaigns-loaded', start)}Methods
| Method | Signature | What it does |
|---|---|---|
applyDiscountCode | (codes: string | string[]) => Promise<void> | Re-fetches campaigns with these codes and writes them to the cart. Codes are merged with any already stored for the session. |
reset | () => Promise<void> | Clears the stored campaign link and codes, blanks the _hype_link cart attribute, empties campaigns / bundles / offers, and re-emits the three loaded events with empty arrays. |
formatMoney | (cents: number, options?: { shouldConvertToMarketPrice?: boolean }) => string | Formats cents using the shop's money format. Converts to the visitor's market price by default. |
updateAllDynamicText | () => void | Re-runs live pricing against the current DOM. Does nothing unless live pricing is active on the page. |
log | (options?: { level: 'info' | 'warn' | 'error' }, ...args: unknown[]) => void | Prints a prefixed console message. Silent unless Enable debug logs is on in the app embed. |
await window.Hype.applyDiscountCode('SUMMER20')await window.Hype.applyDiscountCode(['SUMMER20', 'FREESHIP'])window.Hype.formatMoney(1999) // "$19.99" with a "${{amount}}" money formatwindow.Hype.formatMoney(1999, { shouldConvertToMarketPrice: false })window.Hype.log({ level: 'warn' }, 'No tier matched', cart.item_count)reset() does not remove a code that is already on the cart. To do that, call window.Hype.utils.replaceCartDiscount('').
Properties
| Property | Type | Notes |
|---|---|---|
campaigns | ActiveCampaign[] | undefined | undefined until the proxy responds; [] when nothing is active |
bundles | FixedBundle[] | undefined | Same lifecycle as campaigns |
offers | StorefrontOffer[] | undefined | Same lifecycle as campaigns |
cart | reactive store | get() returns the cart or null; subscribe(cb) returns an unsubscribe function |
events | object | The event-name constants in the table below |
utils | object | The helpers in the last section |
visitorId | string | A persistent per-visitor ID |
Each entry in campaigns carries campaignId, discountCode, type, dynamicPricingEnabled, isPrivateActive and a discounts array whose shape depends on type. type is one of MULTI_VALUE_DISCOUNT, AMOUNT_OFF_ORDER, GIFT_WITH_PURCHASE, TIERED_CART_DISCOUNT or VOLUME_DISCOUNT.
The cart store calls your callback once immediately, then on every change:
const stop = window.Hype.cart.subscribe((cart) => { if (!cart) return console.log(cart.item_count, window.Hype.formatMoney(cart.total_price))})// laterstop()Events
Every event is dispatched on document as a CustomEvent. Listen with document.addEventListener.
| Constant | Event name | Fires when | event.detail |
|---|---|---|---|
CAMPAIGNS_LOADED | hype:campaigns-loaded | Campaigns come back from the app proxy, and on reset() | ActiveCampaign[] |
BUNDLES_LOADED | hype:bundles-loaded | Bundles come back in the same response | FixedBundle[] |
OFFERS_LOADED | hype:offers-loaded | Offers come back in the same response | StorefrontOffer[] |
DISCOUNT_APPLIED | hype:discount-applied | Codes have been written to the cart | { discountCodes: string[], isNewLinkOpened: boolean, isNewDiscount: boolean, campaignId?: string } |
CART_UPDATE_COMPLETE | hype:cart-update-complete | Hype has re-fetched the cart after a change | The cart object |
FREE_GIFTS_ADDED | hype:free-gifts-added | Hype auto-added gift lines | The added line items |
FREE_GIFTS_REMOVED | hype:free-gifts-removed | Hype removed gift lines that are no longer earned | { campaignId: string, items: CartItem[] } |
ITEM_ADDED_TO_CART | hype:item-added-to-cart | A Hype element added items to the cart | The added line items |
document.addEventListener('hype:discount-applied', (event) => { console.log('applied', event.detail.discountCodes)})window.Hype.events also contains CART_UPDATE (hype:cart-update). Nothing in the app dispatches it — use hype:cart-update-complete instead.
Cart and product utilities
All of these live on window.Hype.utils.
| Utility | Signature | Notes |
|---|---|---|
getProductData | (handle: string) => Promise<Product> | Fetches /products/<handle>.js. Cached for 10 minutes in the browser. |
getProductHandleFromUrl | (url: string) => { handle: string | null, variantId: number | null } | Accepts an absolute URL or a path |
getCurrencyRate | () => number | Reads Shopify.currency.rate |
addToCart | (items: AddCartItem[]) => Promise<{ items: CartItem[] }> | Posts to /cart/add.js. Throws with Shopify's message when the add fails, for example a sold-out variant. |
fetchCart | () => Promise<Cart> | Reads /cart.js |
removeFromCart | (lineKeys: string[]) => Promise<Cart> | Sets each line key's quantity to 0 |
replaceCartDiscount | (discount: string) => Promise<Cart> | Replaces the cart's discount codes. Pass '' to clear them. |
const { handle } = window.Hype.utils.getProductHandleFromUrl(location.href)const product = await window.Hype.utils.getProductData(handle)await window.Hype.utils.addToCart([{ id: product.variants[0].id, quantity: 1 }])const cart = await window.Hype.utils.fetchCart()await window.Hype.utils.removeFromCart([cart.items[0].key])removeFromCart takes line keys (cart.items[n].key), not line numbers.
Applying link and code campaigns
Link campaigns. A link campaign activates from the ?hypecampaign=<slug> URL parameter. Hype stores the slug in sessionStorage under hype-campaign and on the cart as the _hype_link attribute, so it survives page loads and returns visits within the session. When more than one source has a value, the URL parameter wins, then session storage, then the cart attribute.
To activate one from your own code, send the shopper to a URL carrying the parameter:
location.href = `/collections/all?hypecampaign=${slug}`Code campaigns. Call applyDiscountCode. Hype re-fetches campaigns with those codes, stores them in sessionStorage under hype-discount-codes, and writes them to the cart:
document.querySelector('#promo-form').addEventListener('submit', async (e) => { e.preventDefault() await window.Hype.applyDiscountCode(e.target.code.value.trim())})Only one code set is written at a time, and Hype replaces the cart's existing codes when it applies. Hype's own cart requests carry a from-hype=1 parameter so they do not re-trigger its cart monitor; keep that in mind if you inspect network traffic.
Related
Was this article helpful?
Related articles
Still stuck?
Ask a follow-up and we'll see it came from “Storefront SDK (window.Hype)”.
Opens chat with your question and a link to this article.