Skip to content

Liquid / Page Builders

The Kaching Bundles widget automatically appears above the Add to Cart button on product pages — no code changes required. The rest of this guide covers integrations where you need more control: custom placement, variant syncing, cart handling, and reading widget state.

By default the widget auto-places itself above the Add to Cart button. To control where it renders — for example inside a page builder section or below the product title — add this element wherever you want the widget:

<kaching-bundle product-id="{{ product.id }}"></kaching-bundle>

When the widget finds a <kaching-bundle> element on the page, it skips auto-placement and renders inside your custom element instead.

If you need to delay initialization — for example, until after your page builder has rendered — set this flag before the Kaching script loads:

window.kachingBundlesDisableAutoInitialize = true

Then call window.kachingBundlesInitialize() manually when ready:

window.kachingBundlesInitialize()

Fired after the widget is fully mounted and ready. Useful for page builders that need to run code after the widget is available.

document.addEventListener("kaching-bundles-block-loaded", (event) => {
const { component } = event.detail
// `component` is the <kaching-bundles-block> HTMLElement
})

If your page builder has its own variant picker, keep the Kaching widget in sync by setting currentVariantId whenever the selected variant changes:

const widget = document.querySelector("kaching-bundles-block")
widget.currentVariantId = selectedVariant.id

currentVariantId accepts a numeric Shopify variant ID.

The widget fires a variant-selected event when the customer selects a variant inside the widget (e.g., when a bundle includes variant options). Use this to update product images or other UI:

widget.addEventListener("variant-selected", (event) => {
const { variantId } = event.detail
updateProductImages(variantId)
})

If your theme has its own quantity picker, keep the widget in sync by setting quantity whenever the value changes:

const widget = document.querySelector("kaching-bundles-block")
widget.quantity = quantityInput.value

By default, the widget hides the theme’s native quantity input to avoid a duplicate UI. To keep it visible, set this flag before the Kaching script loads:

window.kachingBundlesKeepQuantityInput = true

Use the .pricing() method to read current pricing for a one-time display — for example on a custom ATC button:

const widget = document.querySelector("kaching-bundles-block")
const { discountedPrice, fullPrice } = widget.pricing()
atcButton.textContent = `Add to Cart — ${discountedPrice.formatted}`
widget.pricing(): Pricing
{
discountedPrice: { amount: number; formatted: string }
fullPrice: { amount: number; formatted: string }
discountedPricePerItem: { amount: number; formatted: string }
fullPricePerItem: { amount: number; formatted: string }
discountedPriceWithoutSellingPlan: { amount: number; formatted: string }
discountedPricesForSellingPlans: Array<{
sellingPlanId: number
amount: number
formatted: string
}>
}

formatted strings use the store’s currency format (e.g., "$29.99"). Amounts are in currency units (e.g., 29.99 for $29.99).

For reactive updates, listen to the items-changed event — see Reacting to selection changes.

Fired whenever the selection changes — when the customer picks a different tier, changes quantity, or selects bundle products. The event carries no detail; call .pricing() or .items() to read the current state:

const widget = document.querySelector("kaching-bundles-block")
widget.addEventListener("items-changed", () => {
const { discountedPrice } = widget.pricing()
atcButton.textContent = `Add to Cart — ${discountedPrice.formatted}`
})

Fired when a deal bar tier is selected or deselected.

const widget = document.querySelector("kaching-bundles-block")
widget.addEventListener("deal-bar-selected", (event) => {
const { dealBarId, preselected } = event.detail
if (dealBarId) {
console.log("Tier selected:", dealBarId)
} else {
console.log("Tier deselected")
}
})
detail: {
dealBarId: string | null // null when deselected
preselected: boolean // true if the tier was auto-selected
}

By default, the widget handles adding items to the cart — and we strongly recommend keeping it that way. The built-in handling covers edge cases like selling plans, cart properties, and discount validation that are easy to get wrong.

Only disable it if your page builder absolutely must manage the cart itself.

Set this flag before the Kaching script loads:

window.kachingBundlesDisableAddToCartHandling = true

Before adding to cart, validate that the customer has made a valid selection:

const widget = document.querySelector("kaching-bundles-block")
const { valid, message } = widget.validateItemSelection()
if (!valid) {
// The widget already shows the error UI — optionally show `message` in your own UI too
return
}
widget.validateItemSelection(): { valid: boolean; message: string | null }

If valid is false, message contains the merchant-configured error text (or null if none is set).

For silent checks (e.g., disabling a button without showing error UI), use .isItemSelectionValid() instead:

widget.isItemSelectionValid(): boolean

Use .validateItemSelection() when the customer attempts to add to cart, and .isItemSelectionValid() for silent checks.

When the customer clicks your ATC button, call .items() on the widget to get the cart line items:

const widget = document.querySelector("kaching-bundles-block")
const lines = widget.items()
widget.items(): Item[]

Each item has the following shape:

{
id: number // Shopify variant ID
quantity: number
properties: Record<string, unknown>
selling_plan?: number // Selling plan ID, if subscriptions apply
}

Pass lines to your cart API or page builder’s cart action.