Bundle SDK
Reference for the Elite Bundle Builder SDK: live bundle state, steps, cart, discount and actions for custom Shopify bundle storefronts.
Live bundle state and actions for custom storefront UIs. One SDK covers both bundle types: build-your-own bundles and multi-option bundles.
Available after the elite:byob:ready event fires. null when no bundle is on the current page, and the whole sdk object is absent unless SDK access is enabled in Settings → Developer SDK.
const sdk = window.eliteBundle.sdk?.byob;Read the state fresh each time you use it. The object is replaced on every update, so a reference you stashed earlier will go stale.
Build-your-own and multi-option, one shape
A multi-option bundle is a bundle whose every step holds exactly one item. That is why there is no separate MOB SDK — the same steps, cart and checkout describe both:
steps
the steps the shopper moves through
the options the bundle is built from
cart
everything the shopper picked
one line per option
pick an item
addToCart (adds / increments)
selectOne (replaces that option's pick)
add to cart
checkout()
the theme's own Add to cart button, which the app hooks
Read steps[i].optionMode to tell how an option is filled: "single_product_options" means the shopper builds a variant from one product's own options (Colour → Size); anything else means they pick from a list.
State
bundleId
string — Shopify product ID of the bundle.
steps
Step[] — the steps that show for the current picks (options, for a multi-option bundle), each with its fetched products. A step whose Show this step condition doesn't hold is left out, and changes as the shopper picks. A step set to Show it locked stays in, with locked: true and unlockRemaining (items, or money in the shopper's currency, still needed): show its products, but canAddToStep is false for it and its picks and minimum don't count.
The limits live under rules, not on the step itself. An "amount" limit is in the shop's currency, before discounts; compare it with cart prices times window.Shopify.currency.rate.
When
showVariantAsIndividualCardis on, each variant is surfaced as its own entry with a syntheticidand a title suffixed by the variant name. UsesourceProductIdwhenever you need the real product.
cart
CartItem[] — the current selection.
cartTotal · cartTotalFormatted · cartCount
number · string · number — the sum of price × quantity, that sum run through the store's money format, and the total item count. Free gift items (isGift) are left out of all three: they cost nothing and count toward no tier or limit.
On a subscription,
cartTotalandcartTotalFormattedare the bundle's total on the chosen plan, before the bundle discount. This applies while the shopper has a subscription selected in the bundle's purchase-options picker (see Subscriptions). Tier progress uses the same total. ThecartTotalin theelite:byob:cart-changeevent follows the same rule.
addOnsTotal
number — the add-on fees of the cart's items, in the shopper's currency. It is not in cartTotal: add it for the amount the shopper pays. The bundle discount never applies to it.
canCheckout
boolean — every requirement is met: the bundle-wide minimum, plus the minimum of each step that is showing. Gate your add button on this.
bundleAddOns · details · detailsComplete
Array<{ id, title, type, required, choices, showIf? }> · Record<string, string> · boolean — the bundle add-ons, filled in once for the whole bundle (Offer → Add-ons: gift message, To & From, delivery date), titles and choices in the shopper's language; what the shopper has filled in so far (add-on id → text, a choice's id, or "1" for a ticked checkbox); and whether every required one is filled in. type is one of text, textarea, email, date (YYYY-MM-DD), checkbox, radio.
showIf ({ addOnId, choiceId? }) marks one that shows only while the add-on addOnId, earlier in the list and itself shown, is filled in (a ticked box, any text, an address that looks like an email, a date) or, with choiceId, set to that choice. Render it only then: while hidden it isn't required, so detailsComplete ignores it, and its answer isn't sent or charged even if setDetails passed one.
isTierMaxReached
boolean — the cart has reached the highest quantity tier, so there is no further discount to unlock. Only meaningful for quantity-based tiers.
isLoading · error
boolean · string | null — error is a single shopper-facing message; the diagnostic detail goes to the browser console.
isCheckingOut · checkoutError
boolean · string | null — in-flight and failure state for the add to cart.
discountConfig
DiscountSettings — the bundle's discount.
minValue is a count when tierBasedOn is "quantity" and a money amount when it is "order_total". A multi-option bundle's flat discount is expressed as a single tier with minValue: 0, so it always applies.
globalRules
GlobalRules — the bundle-wide rules from Config → Rules → Whole bundle.
An "amount" limit is in the shop's currency, before discounts. You rarely need to check it yourself: canCheckout and canAddToStep already do. Read it to show a message such as "Spend $20 more":
Actions
formatPrice(amount)
Formats an amount using the store's money format.
addToCart(product, variantId, stepId?, addons?, quantity?)
Adds one unit, or quantity. stepId defaults to product.stepId. With addons, the same variant answered differently is a separate cart item. A required add-on is not checked here: check it before calling, as quick view does.
It can silently do nothing — when the bundle-wide maximum is reached, when the step's own limit is reached, or when the step allows only one of each product and that product is already in. Check canAddToStep(stepId, price) first if you need to know.
selectOne(product, variantId, stepId?)
Multi-option bundles. Makes this the step's one and only pick, replacing whatever was selected before. Use this rather than addToCart for options — addToCart would leave two items in the same option.
removeFromCart(variantId, stepId?) · updateQuantity(variantId, delta, stepId?, itemId?)
Remove a line, or change its quantity by delta. A quantity of zero or less removes the line. updateQuantity is subject to the same limits as addToCart and will no-op rather than exceed them. One variant can be several items (each with its own add-on answers): pass the item's id as itemId to change that one; without it, the variant's latest item changes.
getItemQuantity(variantId, stepId?)
number — how many of this variant are in the cart.
isStepFull(stepId) · canAddToStep(stepId, price?)
boolean — whether the step has hit its own maximum, and whether one more item may be added. canAddToStep also accounts for the bundle-wide maximum, so it can be false even for a step with no limit of its own. Pass the variant's price for amount limits: a $30 item may not fit where a $10 one does. On a free gift step only the step's own limit applies, once the bundle has the paid items the gift needs.
setDetails(details)
Replaces what's filled in for bundleAddOns. It goes to the cart with the bundle, on its line, and is cleared once it's added.
checkout(redirectTarget?)
Adds the whole bundle to the Shopify cart. Only call it when canCheckout and detailsComplete are true: with a required bundle add-on empty it sets checkoutError and adds nothing.
redirectTarget
Behaviour
"stay_on_page" (default)
Stays put and asks the theme to open its cart drawer
"cart"
Redirects to /cart
"checkout"
Redirects to Shopify checkout
On success the bundle is emptied and a new bundle session begins, so a stay_on_page UI must be ready to re-render from an empty cart.
On a subscription, checkout() adds every item as its own cart line with its selling_plan and the bundle's Bundle_ID property, because Shopify does not merge lines that carry a selling plan. A one-time purchase is still one bundle line. The SDK has no method to pick a plan: checkout() uses the plan chosen in the built-in picker, or else the bundle's default from Config → Subscriptions.
Translated text
title and description are TranslatableString — either a plain string, or { default, translations } when the merchant has translated them. Rendering one directly can print [object Object]:
Full example
See the Events Reference for every event and its payload.
Last updated
Was this helpful?
