Guided bundles: JavaScript events and methods

BYOB guided bundles provide JavaScript events and a getState() method for custom storefront integrations.

You can use them to track customer actions, display a custom bundle summary, update progress labels, or build custom navigation controls such as a sticky footer.

These events apply to the BYOB Guided Bundle configurator. For full-page bundles, see Full-page bundle callbacks and methods.

Where to put your code

Open the Shopify theme editor and select the product template containing your BYOB Guided Bundle block.

Add a Custom Liquid section with your JavaScript inside <script> tags. Place it before the guided bundle block so your listeners can capture initialization events.

You can also place the code in your theme’s JavaScript files. Keep custom code in your theme rather than editing BYOB’s extension assets.

Available events

Listen for events using document.addEventListener(). The payload is available through event.detail.

Every event includes event.detail.state, which contains the current bundle snapshot.

Event When it fires Additional payload
byob:configurator:ready The configurator is initialized and its state getter is available None
byob:configurator:step-changed The displayed step changes previousStep
byob:configurator:selection-changed Selected items or their quantities change changes, reason
byob:configurator:state-changed Bundle state changes, including selections, pricing, step, or button availability None
byob:configurator:review-shown The customer enters the review screen None
byob:configurator:added-to-cart A bundle is successfully added to cart, before the cart drawer updates or the page redirects submittedItems, cartResponse

For example:

<script>
document.addEventListener('byob:configurator:step-changed', function(event) {
  const step = event.detail.state.currentStep;

  console.log('Step ID:', step.id);
  console.log('Step index:', step.index);
  console.log('Step title:', step.title);
  console.log('Total steps:', step.totalSteps);
  console.log('Review screen:', step.isReview);
});
</script>

Step indexes start at 0. totalSteps excludes the review screen. Use isReview to identify review rather than displaying it as another selection step.

Reading the current state

Use getState() when your script needs the current state without waiting for the next event:

const container = document.querySelector('.byob-bundle-configurator-app');

if (container && container.byobConfigurator) {
  const state = container.byobConfigurator.getState();
  console.log(state);
}

The getter becomes available when the configurator is ready. It returns a copy of the latest completed state and does not change the bundle.

For scripts that may load before or after initialization, use both event listeners and the getter:

<script>
(function() {
  function updateSummary(container, state) {
    let totalText = 'Calculating&hellip;';

    if (state.pricingStatus === 'ready') {
      totalText = new Intl.NumberFormat(undefined, {
        style: 'currency',
        currency: state.currencyCode
      }).format(state.totalCents / 100);
    } else if (state.pricingStatus === 'error') {
      totalText = 'Price unavailable';
    }

    // Replace these logs with updates to your custom UI.
    console.log('Bundle:', state.bundleId);
    console.log('Total:', totalText);
    console.log('Item count:', state.itemCount);
    console.log('Next enabled:', state.canGoNext);
  }

  function handleState(event) {
    updateSummary(event.target, event.detail.state);
  }

  document.addEventListener('byob:configurator:ready', handleState);
  document.addEventListener('byob:configurator:state-changed', handleState);

  document.querySelectorAll('.byob-bundle-configurator-app')
    .forEach(function(container) {
      if (container.byobConfigurator) {
        updateSummary(container, container.byobConfigurator.getState());
      }
    });
})();
</script>

After triggering a button, wait for state-changed to read the result. Selection rules and UI updates are processed before the new state is published.

State fields

These fields are available in getState() and event.detail.state.

Field Description
apiVersion Public API version. Currently 1
instanceId Identifier for this mounted configurator
bundleId Bundle ID as a string, or null
isDesignMode Whether the configurator is running in Shopify’s theme editor
currentStep Current step’s id, index, title, totalSteps, and isReview
selectedItems List of selected item lines
itemCount Total selected variant quantity
subtotalCents Total before the BYOB bundle discount
totalCents Total after the BYOB bundle discount
currencyCode Currency used for totals, such as USD
pricingStatus loading, ready, or error
currentStepRequirementMet Whether the current selection step’s requirement is met; null on review
allStepsRequirementsMet Whether all selection steps meet their requirements
canGoBack Whether the current native Back button is available and enabled
canGoNext Whether the current native Next button is available and enabled
canAddToCart Whether the current native Add to Cart button is available and enabled
isAddingToCart Whether a cart request is in progress

Prices and quantities

Divide money values by 100 before formatting them in the supplied currency. For example, totalCents: 2500 represents an amount of 25.While prices load, totals may be null and pricingStatus will be loading. A subsequent state-changed event supplies the resolved total. A failed calculation uses pricingStatus: 'error'.

Zero is a valid total. null does not mean zero.

Totals match the configurator’s review estimate. They exclude shipping, taxes, and additional cart or checkout adjustments.

itemCount counts quantities, not distinct products. Selecting three units of one variant gives an item count of three. Virtual choices do not contribute to this count.

Button availability mirrors BYOB’s native controls. It does not guarantee cart submission will succeed: the app still checks access, draft status, and cart errors.

Selected item fields

Each entry in selectedItems contains:

Field Description
stepId Step containing the selection
optionId Selected option’s ID
type Configured option type
title Item’s display title
productId Shopify product ID
variantId Shopify variant ID
handle Product handle
quantity Selected quantity
unitPriceCents Unit price before the bundle discount
currencyCode Currency for the unit price

Shopify IDs are strings. Product metadata and prices may be null while loading or when unavailable.The same variant selected in different steps appears as separate lines. Identify a line using stepId, optionId, and variantId together.

Virtual choices have no Shopify product or variant where those fields do not apply.

Tracking selection changes

The selection-changed event includes a changes array. Each change contains:

Field Description
item The affected item, using the selected-item fields above
action added or removed
previousQuantity Quantity before the change
quantity New quantity; zero for complete removal
quantityDelta New quantity minus previous quantity

For example:

<script>
document.addEventListener('byob:configurator:selection-changed', function(event) {
  console.log('Change reason:', event.detail.reason);

  event.detail.changes.forEach(function(change) {
    console.log('Variant:', change.item.variantId);
    console.log('Action:', change.action);
    console.log('New quantity:', change.quantity);
    console.log('Quantity delta:', change.quantityDelta);
  });
});
</script>

Increasing quantity from one to three reports added with quantityDelta: 2. Decreasing it from three to one reports removed with quantityDelta: -2.

Replacing a variant reports removal of the previous variant and addition of the new one. For complete removal, item retains the previous item details; use change.quantity and change.quantityDelta to interpret the change.

The event also provides a reason:

Reason Meaning
user A customer selection action
back Backward navigation cleared later selections
reset Reset cleared selections
rule Configurator rules changed selections
mixed Multiple origins contributed to the same update

Going backward can therefore emit both step-changed and selection-changed.Rejected selections and unchanged quantities do not emit selection changes. Price updates alone emit state-changed, without reporting another product addition.

Running code after adding to cart

<script>
document.addEventListener('byob:configurator:added-to-cart', function(event) {
  console.log('Submitted items:', event.detail.submittedItems);
  console.log('Cart response:', event.detail.cartResponse);

  // Add your custom code here.
});
</script>

submittedItems records the selections submitted with that request. cartResponse is Shopify’s successful add response, not the complete cart.

Failed requests do not emit this event. It confirms an addition to cart, not a completed purchase.

isAddingToCart may still be true during the success event. A later state update reflects completion of the submission handler.

Using custom navigation buttons

Custom controls can trigger BYOB’s existing buttons so validation remains in the app.

Use these selectors inside the configurator container:

Action Selector
Back [data-byob-action="back"]
Next [data-byob-action="next"]
Add to Cart [data-byob-action="add-to-cart"]
function triggerBYOBAction(container, action) {
  const button = container.querySelector(
    '[data-byob-action="' + action + '"]'
  );

  if (button && !button.disabled) {
    button.click();
  }
}

Pass back, next, or add-to-cart as the action.

For a sticky footer, use the state’s availability fields to enable or disable your controls. Switch Next to Add to Cart when currentStep.isReview is true.

Find the native button on each click rather than saving its element permanently, because changing steps can replace it.

Important integration notes

  • Events provide notifications. Returning false or calling preventDefault() does not block BYOB actions.
  • Initialization emits ready and state-changed. Use these to read initial state; step-changed describes subsequent step changes.
  • review-shown fires each time the customer enters review.
  • Several events can describe the same update. For analytics, avoid counting a selection event and its state update as separate selections.
  • Events listened to on document can come from any configurator instance. event.target identifies the source container.
  • For analytics, check state.isDesignMode to exclude theme-editor interactions.

Still need help? Contact Us Contact Us