Skip to content
Kitenzo Headless is currently invite-only. To enable it on your store, email support@kitenzo.com.

Web component

The <bundle-builder-bundle-v1> custom element renders a full bundle. Load the script and stylesheet, then place the element with your API key.

<!-- Kitenzo storefront styles -->
<link rel="stylesheet" href="https://live.bb.eight-cdn.com/bundle-builder-bundle.css" />
<!-- Kitenzo storefront script -->
<script src="https://live.bb.eight-cdn.com/bundle-builder-bundle.js" async type="module"></script>
<!-- Render the bundle -->
<bundle-builder-bundle-v1
id="42"
api-key="kit_live_your_api_key_here"
prefix="https://live.bb.eight-cdn.com/api/headless/v1/embed"
data='{"shop":{"domain":"your-store.myshopify.com","shopCurrency":"USD","cartCurrency":"USD","enabledCurrencies":["USD"],"moneyFormat":"${{amount}}"}}'
>
<div class="bundle-builder-app--loading-spinner"><div></div></div>
</bundle-builder-bundle-v1>
AttributeTypeRequiredDescription
idstringYes*Bundle ID to load.
handlestringYes*Shopify product handle (alternative to id).
api-keystringYes**Headless API key (kit_live_… / kit_test_…).
prefixstringNoAPI base URL. For headless, set to https://live.bb.eight-cdn.com/api/headless/v1/embed.
datastringNoJSON with { shop, settings, bundle }. Bypasses the in-theme #bundle-builder--shop script tag and (when bundle is included) the API fetch.
full-page"yes" | "no"NoWhether to update the page title and canonical URL.

* One of id or handle is required. ** Required for headless storefronts. Not needed in Liquid themes (which use the app proxy).

In React frameworks, load the assets in an effect and render the element (note: custom elements need the @ts-expect-error escape hatch in TSX):

import { useEffect, useRef } from 'react';
const KITENZO_APP_URL = 'https://live.bb.eight-cdn.com';
function KitenzoBundleEmbed({ bundleId }: { bundleId: number }) {
const loaded = useRef(false);
const data = JSON.stringify({
shop: {
domain: import.meta.env.VITE_SHOP_DOMAIN,
shopCurrency: 'USD',
cartCurrency: 'USD',
enabledCurrencies: ['USD'],
moneyFormat: '${{amount}}',
},
});
useEffect(() => {
if (loaded.current) return;
loaded.current = true;
if (!document.querySelector('link[data-kitenzo-styles]')) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = `${KITENZO_APP_URL}/bundle-builder-bundle.css`;
link.dataset.kitenzoStyles = 'true';
document.head.appendChild(link);
}
if (!document.querySelector('script[data-kitenzo-bundle]')) {
const script = document.createElement('script');
script.src = `${KITENZO_APP_URL}/bundle-builder-bundle.js`;
script.type = 'module';
script.async = true;
script.dataset.kitenzoBundle = 'true';
document.head.appendChild(script);
}
}, []);
return (
// @ts-expect-error Custom element
<bundle-builder-bundle-v1
id={String(bundleId)}
api-key={import.meta.env.VITE_KITENZO_API_KEY}
prefix={`${KITENZO_APP_URL}/api/headless/v1/embed`}
data={data}
>
<div className="bundle-builder-app--loading-spinner"><div /></div>
</bundle-builder-bundle-v1>
);
}

The bundle renders with its admin-configured template styles. Override with CSS:

.bundle-builder--wrapper {
max-width: 1200px;
margin: 0 auto;
}
.bundle-builder--theme-app-extension {
/* your overrides */
}