---
title: "Developer Javascript and events"
description: "Some events we emit so you can build additional logic around them"
url: https://help.biscuitsbundles.com/en/articles/9045771-developer-javascript-and-events
collection: "Developer Documentation"
updated: 2026-10-01
language: en
---

# Developer Javascript and events

Some events we emit so you can build additional logic around them

Biscuits Bundles components communicate through standard DOM `CustomEvent`s and expose runtime configuration on the global `window.BiscuitsBundle` object. This page lists every event you can listen for and the most useful global hooks.

## Ready events

Each component fires a one-time `:ready` event when it finishes initialising. All bubble and carry no `detail` payload.

<table data-id="2264a693-fba1-47b4-b5bb-78446b992bcc" style="min-width: 427px;"><colgroup><col style="width: 402px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p data-id="eb0a5aa3-c086-434e-9412-87e489bd3af9">Event</p></td><td colspan="1" rowspan="1"><p data-id="bf6f157a-5d0c-49a7-be9e-0ccb99bd6386">Fired by</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="345d38ac-ecb0-4854-91df-fa2ab780aab3"><code>BiscuitsBundleForm:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="6a7e6713-bb52-4de0-aa65-3b2ee841dad2"><code>&lt;biscuits-bundle-form&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="783b0c75-fdd6-4785-bd33-9475d33b68c5"><code>BiscuitsBundleStep:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="6c6d4a50-a409-41a0-b726-d327d80f5445"><code>&lt;biscuits-bundle-step&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="b5457e8a-be11-4b6e-a8f9-ab3bfafb505c"><code>BiscuitsBundleVariant:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="2304efdd-0105-45d8-93f4-72cdd129e446"><code>&lt;biscuits-bundle-variant&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="e4d81e3c-17c6-4945-a548-c4cacf5fdfc4"><code>BiscuitsBundleProduct:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="4227e2fa-59e5-4d1b-83d7-1fda3598a067"><code>&lt;biscuits-bundle-product&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="9c0182c2-82e4-4277-ac18-5b067ba532f9"><code>BiscuitsBundleQuantity:ready</code></p></td><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="230c3dd6-34c9-438d-bbda-020ab9e81427"><code>&lt;biscuits-bundle-quantity&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="8f2810dc-1935-47cc-a9c1-1e70125bea6a"><code>BiscuitsBundlePrice:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="ed2a32d9-d91f-41c2-bcc1-0b9709e31fb9"><code>&lt;biscuits-bundle-price&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="21cc6788-bee7-480e-b439-33e98b71ef37"><code>BiscuitsBundleSummary:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="371c5286-fdb8-43b4-a756-5a4f05039f27"><code>&lt;biscuits-bundle-summary&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="51b830bb-f597-454f-b399-1bd676761a0d"><code>BiscuitsBundleAddToCart:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="e256a8fe-6ffb-4c91-8ac7-eb5b019883a4"><code>&lt;biscuits-bundle-add-to-cart&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="96dcc46e-fc9b-437c-adfc-ec9fe2d61b04"><code>BiscuitsBundleAccordionSummary:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="1d513260-ebf4-425a-8605-4bbcecc23db3"><code>&lt;biscuits-bundle-accordion-summary&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="f8f47dc9-c627-4ad8-a0f5-461aad57269c"><code>BiscuitsBundleVolumeDiscounts:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="e104dcb5-2e21-4703-acb7-6f76c73df181"><code>&lt;biscuits-bundle-volume-discounts&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="3a163e6d-8799-4261-a3ff-3cb136d8da95"><code>BiscuitsBundleVolumeDiscountTier:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="c9373620-bd39-4299-8f90-ceeb981e8f30"><code>&lt;biscuits-bundle-volume-discount-tier&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="b4c2cb62-d8b1-4b98-9d90-c394069caa16"><code>BiscuitsBundleRelatedBundles:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="3847f416-54e8-4b05-89da-57c6363ae735"><code>&lt;biscuits-bundle-related-bundles&gt;</code></p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="6360637d-620c-46c1-8354-3a4298ecdc8e"><code>BiscuitsBundleCustomFieldGroup:ready</code></p></td><td colspan="1" rowspan="1"><p data-id="701e1107-976a-46c6-ad11-37bc768350d1"><code>&lt;biscuits-bundle-custom-field-group&gt;</code></p></td></tr></tbody></table>

## App-level event

<table data-id="8ed4f232-31b8-4537-8431-88cc439de366" style="min-width: 428px;"><colgroup><col style="width: 222px;"><col style="width: 181px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p data-id="f4ae4bed-965f-48b9-9e1b-8cf01ac99649">Event</p></td><td colspan="1" rowspan="1"><p data-id="806e701b-0c2e-446c-9992-093954e0fbbc">Fired on</p></td><td colspan="1" rowspan="1"><p data-id="a58d4e87-919f-439a-a72d-e11c5a2d8eb7">When</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="f9f737d3-5f59-493d-a240-14ba64621a7f"><code>BiscuitsBundle:allReady</code></p></td><td colspan="1" rowspan="1"><p data-id="b8e0ea0d-eb37-4696-b7e3-8a6029153a63">the <code>&lt;biscuits-bundle-form&gt;</code> element <strong>and</strong> <code>document</code></p></td><td colspan="1" rowspan="1"><p data-id="bd9817ed-6615-4b27-a021-76f85d28a747">After every sub-component is ready and the bundle has finished its first render. This is the safe entry point for custom code.</p></td></tr></tbody></table>

## Interaction events

<table data-id="d1d653dd-a99f-4b86-92dd-acbfd107d197" style="min-width: 308px;"><colgroup><col style="width: 258px;"><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p data-id="75a846c2-112f-42c7-a3e4-15bca509d23c">Event</p></td><td colspan="1" rowspan="1"><p data-id="1afadfb2-c19a-4481-89a9-3eb3bbfa1f2c"><code>detail</code> payload</p></td><td colspan="1" rowspan="1"><p data-id="ff6bbd48-647f-48a0-99b7-74dd8dbbe912">When</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="fe1f57cd-93b8-46ca-b432-f8ca655d09d7"><code>biscuits--product-selection</code></p></td><td colspan="1" rowspan="1"><p data-id="baba112a-6334-4592-ac55-236599df9dda"><code>{ variant_id, price, compare_at_price, quantity, active, max, step, product_id, uniqueCode, product_title, variant_title, quantity_available }</code></p></td><td colspan="1" rowspan="1"><p data-id="a53d5c44-9788-4819-ae4e-de759407579a">A product/variant is selected or deselected. <code>product_id</code> is only present for multi-variant products.</p></td></tr><tr><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="22ffe3da-6b6e-440a-84e8-479713fa3def"><code>biscuits--quantity-updated</code></p></td><td colspan="1" rowspan="1"><p data-id="b129a41d-f11b-481e-86d8-f34ac9743efa"><code>{ quantity, fixedQuantity }</code></p></td><td colspan="1" rowspan="1"><p data-id="e2825f77-b14b-453d-b839-4eef9e1d8de4">A quantity control value changes and passes validation.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="40251877-1304-4b6e-981f-dce7b459594c"><code>biscuits--step-update</code></p></td><td colspan="1" rowspan="1"><p data-id="384d67a4-0ad7-47bb-b1e1-b265e8292523"><code>{ stepIndex, selectedProducts, totalQuantity, totalSelected, totalPrice, comparePrice, valid, required, maxSelectedReached, maxQuantityReached, minSelectedReached, minQuantityReached }</code></p></td><td colspan="1" rowspan="1"><p data-id="6767fe6f-1a2a-49d4-8687-f01271da2c51">A step's totals/validation change after a selection or quantity change. The primary event to listen to for reacting to selections. Does <strong>not</strong> fire for custom-field changes.</p></td></tr><tr><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="4a9ece35-673a-462c-b3e2-3161130793e3"><code>biscuits--custom-field-change</code></p></td><td colspan="1" rowspan="1"><p data-id="cb06b51a-c446-419f-ba4a-12b24f3526c3"><em>none</em></p></td><td colspan="1" rowspan="1"><p data-id="ac1f1e4c-0527-4544-9bad-6ea5e83c97d1">A custom field's value changes. Read current values from the field elements (it's a signal, not a snapshot).</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="d2085c58-474f-47f2-b4af-7dd120d88dd8"><code>biscuits--accordion-advance</code></p></td><td colspan="1" rowspan="1"><p data-id="54a7563b-b338-4f82-b9f1-8ca212afa942"><code>{ stepIndex }</code></p></td><td colspan="1" rowspan="1"><p data-id="b288494d-f227-45d9-8b07-f42a894e4bab">The accordion auto-advances to the next incomplete step. <code>stepIndex</code> is the step that was opened.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="115ecc31-e019-4f23-93a2-9728c13491e1"><code>biscuits--add-to-cart-success</code></p></td><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="3eb3e047-165a-4d54-a02c-839d1b80970f"><code>{ data }</code> — Shopify's <code>cart/add.js</code> response</p></td><td colspan="1" rowspan="1"><p data-id="2eb06613-ae50-4458-a51a-20d58850f29e">The bundle was added successfully, before any theme cart drawer/refresh runs.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="d9406a37-4ddb-4bef-aafa-b507e21e5a0f"><code>biscuits--add-to-cart-error</code></p></td><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="d220f215-59f4-43c6-b2cd-6b62831d74a9"><code>{ status, message, error }</code></p></td><td colspan="1" rowspan="1"><p data-id="cfffaca3-63e8-432b-a789-7d5566d3bd65">The add-to-cart request failed (non-2xx response or network error). Does not fire on client-side validation errors.</p></td></tr></tbody></table>

## The `window.BiscuitsBundle` global

Useful properties and helpers available at runtime:

<table data-id="12246874-2091-474b-9ef8-2b67b402302a" style="min-width: 298px;"><colgroup><col style="width: 273px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p data-id="5fcca282-61ea-46fe-82fc-cdb56e0f8217">Property</p></td><td colspan="1" rowspan="1"><p data-id="9680a2d5-e35f-4811-966e-ba0dd69dcd50">Description</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="7d4f7fcd-7e94-412e-b4d2-069a4f54e560"><code>formatMoney(cents, format)</code></p></td><td colspan="1" rowspan="1"><p data-id="b83ec79a-6123-4166-bb1b-c753359bdb72">Formats a cents integer into a localised money string.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="f35910b5-9a4a-43c2-a88b-59f3097a877d"><code>moneyFormat</code> / <code>moneyFormatTotal</code></p></td><td colspan="1" rowspan="1"><p data-id="7247bac0-64e3-4c00-bc54-c826df72850c">The shop's money format strings.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="b4dea59c-84f5-4c69-84f1-9b0ec6b1f94e"><code>translations</code></p></td><td colspan="1" rowspan="1"><p data-id="015faec6-cb2f-47aa-90da-7a4e1f3eea79">All app language strings, keyed by translation key.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="2b59f1a6-4358-48aa-97d2-214526d73754"><code>routes</code></p></td><td colspan="1" rowspan="1"><p data-id="01f12f49-3b0e-4bd0-abc2-b3b48bf1b03f"><code>{ cartAddUrl, cartUrl }</code> Shopify route helpers.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="50f96c90-87a3-4b18-bf96-1491f84324c6"><code>bundleStepScrollOffset</code> / <code>bundleSummaryScrollOffset</code></p></td><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="965e0f64-5954-4334-8a1b-f44bce266f0a">Pixel offsets for auto-scroll (default <code>200</code>). Override before init.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="e5072c31-3bc0-4549-b379-28771e8507d4"><code>disableAccordionScroll</code></p></td><td colspan="1" rowspan="1" style="padding: 2px; border: 0px none rgba(228, 228, 228, 0.92);"><p data-id="50df5ff5-e3e5-412b-b431-1883e04ccc1e">Set <code>true</code> to disable accordion auto-scrolling.</p></td></tr><tr><td colspan="1" rowspan="1"><p data-id="944b9664-f177-4f88-a06b-7488378abef1"><code>themeDataOverride</code> / <code>themeDataDefault</code></p></td><td colspan="1" rowspan="1"><p data-id="5ec818a8-c365-4c13-9501-3a0670e0825f">Control post-add-to-cart cart drawer/redirect behaviour.</p></td></tr></tbody></table>

## Localised bundles

All events on this page fire for localised bundles too, including `biscuits--add-to-cart-success`. The add to cart request itself is different though: a localised bundle adds its component lines with their own properties (`_bundleId`, `_selectedItems`, `_biscuitsDisplayTitle`), so the `cart/add.js` response in `event.detail.data` and the resulting cart contents have a different shape. Custom code that reads the cart response, or matches lines by properties like `_biscuits_title`, should be tested against a localised bundle. `_bundleId` is carried on every bundle line for both kinds, so it is the reliable way to identify bundle lines. See [Localised bundles: pricing for Shopify Markets](https://help.biscuitsbundles.com/en/articles/14043181-localised-bundles-pricing-for-shopify-markets.md).

## Common code blocks

**Run code after the bundle is ready**

```javascript
const form = document.querySelector('biscuits-bundle-form');
form.addEventListener('BiscuitsBundle:allReady', () => {
  // All components are initialised — safe to read form.formTotalPrice, etc.
});
```

💡 **Tip:** Make sure you **don't** put this event listener inside a "DOMContentLoaded" as our ready events will trigger before this.

**React to selection changes**

```javascript
const form = document.querySelector('biscuits-bundle-form');
form.addEventListener('BiscuitsBundle:allReady', () => {
  form.querySelectorAll('biscuits-bundle-step').forEach((step) => {
    step.addEventListener('biscuits--step-update', (event) => {
      // event.target is the <biscuits-bundle-step>
      // event.detail.totalPrice, event.detail.valid, etc.
    });
  });
});
```

**React to a successful add-to-cart**

```javascript
const addToCart = document.querySelector('biscuits-bundle-add-to-cart');
addToCart.addEventListener('biscuits--add-to-cart-success', (event) => {
  const cartResponse = event.detail.data; // Shopify cart/add.js response
});
```

**Read the current total**

```javascript
const form = document.querySelector('biscuits-bundle-form');
form.addEventListener('BiscuitsBundle:allReady', () => {
  form.querySelectorAll('biscuits-bundle-step').forEach((step) => {
    step.addEventListener('biscuits--step-update', () => {
      const totalCents = form.formTotalPrice;
      const formatted = window.BiscuitsBundle.formatMoney(totalCents, window.BiscuitsBundle.moneyFormat);
      // formatted holds the current total (after discounts), in the shop's money format
    });
  });
});
```

* * *

## Where does your custom code output?

When using the Custom JS field in the Biscuits Bundles app block, your custom javascript code is placed at the very bottom of our Shopify theme block code. This ensures that you can use event listeners on our custom events and interact with fully loaded DOM elements.

```javascript
<!-- our Biscuits Bundles code -->

<script>
/* Your custom javascript will output here */
</script>

<script src="biscuits-section-bundle-production.min.js" defer></script>
```

Some of the other values you could access on the form are:

-   formCompareAtPrice
    
-   formDiscountValue
    
-   formSavingsAmount
    
-   formSavingsPercentage
    
-   formTotalPrice
    

* * *

## Testing

Always thoroughly test your custom JS to ensure if behaves as expected across all supported browsers and devices.

**Stuck or got questions?**

Use the chat widget below, or reach out to us at [hello@sundaelane.com](mailto:hello@sundaelane.com)

Happy coding, and thanks for teaming up with Biscuits Bundles!
