whim.js
Drop-in script for any website. Web components, JS API, and cart management.
1. Install the SDK
Add the bootstrap snippet and script tag to your page.
<script>window.whim=window.whim||{},function(e){try{var t="whim-initial-url",i=sessionStorage.getItem(t);e.initialUrl=window.whimInitialUrl=i||location.href,i||sessionStorage.setItem(t,e.initialUrl)}catch(e){}void 0===e.isReady&&(e.isReady=!1);var n=e._q=e._q||[];function r(e,t,i,r){var o;i.split(" ").forEach(function(i){e[i]||(e[i]=function(e){return function(){var t,i=new Promise(function(e){t=e});return n.push([e,[].slice.call(arguments),t]),i}}(t+i))}),r.split(" ").forEach(function(i){e[i]||(e[i]=function(e){return function(){n.push([e,[].slice.call(arguments)])}}(t+i))}),e.on||(e.on=(o=t+"on",function(){var e=[o,[].slice.call(arguments)];return e.cancelled=!1,e.realUnsub=null,n.push(e),function(){e.realUnsub?e.realUnsub():e.cancelled=!0}}))}r(e,"","getMode getProducts getCheckoutUrl getPrice getConfig getPortalUrl checkCustomer","setMode setProduct setAddon removeAddon map"),e.ui=e.ui||{},e.ui.ready||(e.ui.ready=function(e){n.push(["ui.ready",[e]])}),e.ui.refresh||(e.ui.refresh=function(){n.push(["ui.refresh",[]])}),r(e.cart=e.cart||{},"cart.","add has get getCheckoutUrl count distinctCount getProductNames","setQuantity remove clear")}(window.whim);</script>
<script async src="https://api.sdk.whim.com/v1/YOUR_MERCHANT_ID.js"></script>
Replace YOUR_MERCHANT_ID with the merchant ID we provide you (e.g. demo.js).
Add this to your page's <head>, before any code that uses the SDK.
Add this to your index.html (or HTML shell). The web components work directly in JSX with React 19+.
- Getters and cart data methods return promises. Use
awaitor.then():
var price = await whim.getPrice();
var inCart = await whim.cart.has('sku-123');
var added = await whim.cart.add('sku-123');
- Setters and
ui.*calls don't return anything:
whim.setMode('whim');
whim.setProduct('sku-123');
whim.ui.ready(function() { renderMyUi(); });
whim.on()andwhim.cart.on()return an unsubscribe function (sync, don'tawait).whim.isReadyis a boolean. Startsfalsewhen the snippet runs, becomestrueonce the SDK loads.
var off = whim.on('price-change', function(price) { renderPrice(price); });
// later: off();
if (whim.isReady) renderMyUi();
else whim.ui.ready(renderMyUi);
2. Set up your product page
Since customers can now use Whim on your products, you'll need a way for them to pick between buying and Whim on the product page. The SDK manages that choice. Whim mode pulls pricing from Whim and redirects checkout to us, buy mode is untouched.
Here's what that looks like on a product page using web components. You drop these into your existing product detail layout. The JS API is also available if you need more control.
Set the product
<whim-selector> tells the SDK which product to load. Everything else goes inside it.
Add the buy/whim toggle
<whim-option> lets shoppers switch between buy and whim mode. The SDK handles the state, selection styling, and accessibility.
Display the price
<whim-price> shows the Whim price in whim mode. Set buy-price to show your buy price when in buy mode.
Handle checkout
<whim-checkout-button> wraps your existing CTA. Redirects to Whim checkout in whim mode.
<!-- your existing product page markup -->
<div class="product-detail">
<img src="/images/wireless-headphones.jpg" alt="Wireless Headphones" />
<h1>Wireless Headphones</h1>
<p class="color">Black</p>
<!-- Whim components go inside your layout -->
<whim-selector product="wireless-headphones-black">
<!-- your own toggle markup, Whim handles the click + state -->
<div class="purchase-options">
<whim-option mode="buy">Buy</whim-option>
<whim-option mode="whim">Rent</whim-option>
</div>
<!-- standalone price display -->
<whim-price buy-price="$249" suffix="/mo" />
<!-- wraps your existing CTA -->
<whim-checkout-button>
<a href="/cart/add?sku=sku-123456" class="btn">Add to Cart</a>
</whim-checkout-button>
</whim-selector>
</div>
// ProductPage.jsx
export default function ProductPage({ product }) {
return (
<div className="product-detail">
<img src={product.image} alt={product.name} />
<h1>{product.name}</h1>
<p className="color">{product.color}</p>
{/* Whim components go inside your layout */}
<whim-selector product={product.whimId}>
{/* your own toggle markup, Whim handles the click + state */}
<div className="purchase-options">
<whim-option mode="buy">Buy</whim-option>
<whim-option mode="whim">Rent</whim-option>
</div>
{/* standalone price display */}
<whim-price buy-price={product.priceFormatted} suffix="/mo" />
{/* wraps your existing CTA */}
<whim-checkout-button>
<button className="btn" onClick={() => addToCart(product.sku)}>
Add to Cart
</button>
</whim-checkout-button>
</whim-selector>
</div>
);
}
Components render in light DOM, so your existing CSS applies to them. See Components for all attributes and styling options.
setMode().
3. Product mapping (optional)
If your site uses its own product identifiers (internal SKUs, variant IDs), register a mapping so you don't have to scatter Whim product names through your code:
whim.map({
'sku-123456': 'wireless-headphones-black',
'sku-789012': 'wireless-headphones-silver',
});
// Now use your own IDs everywhere
whim.setProduct('sku-123456');
whim.cart.add('sku-789012');
whim.cart.has('sku-123456'); // true
Once mapped, your IDs work everywhere: setProduct, cart.add, cart.remove, cart.has. Unmapped IDs pass through as-is, so you can mix Whim product names and your own.
whim.setProduct() with the new SKU. The SDK updates pricing and checkout URLs automatically.
Using the JS API instead
You can also skip the components and wire everything up yourself with the window.whim JS API. Here's the same buy/whim toggle, price, and checkout done in plain JS:
<button id="buy">Buy</button>
<button id="rent">Rent</button>
<span id="price"></span>
<button id="checkout">Add to Cart</button>
<script>
whim.setProduct('wireless-headphones-black');
whim.ui.ready(function() {
async function render() {
var mode = await whim.getMode();
var price = await whim.getPrice();
document.getElementById('price').textContent =
mode === 'whim' && price ? price.priceFormatted + '/mo' : '$249';
document.getElementById('buy').style.fontWeight = mode === 'buy' ? '700' : '400';
document.getElementById('rent').style.fontWeight = mode === 'whim' ? '700' : '400';
}
document.getElementById('buy').onclick = function() { whim.setMode('buy'); };
document.getElementById('rent').onclick = function() { whim.setMode('whim'); };
document.getElementById('checkout').onclick = async function() {
if (await whim.getMode() === 'whim') {
var url = await whim.getCheckoutUrl();
if (url) window.location.href = url;
return;
}
// your buy checkout logic
};
whim.on('mode-change', render);
whim.on('price-change', render);
render();
});
</script>
You can also mix both — use components where they fit and the JS API for anything custom. See the API Reference for the full list of methods, events, and types.
whim.getPrice() or whim.on('mode-change', fn) alongside web components to build custom UI that stays in sync.
API Reference
All methods are available on window.whim. The SDK loads async, and calls made before it loads are queued and replayed automatically. Getters return promises that resolve once the SDK is ready, so you can await them or use .then(). You can also use whim.ui.ready() for callback-style initialization.
With the JS API you write your own HTML, listen to events, and render prices and checkout however you want. Good for when you need to match an existing PDP layout exactly. The Web Components are built on this same API and can be mixed in.
Everything is event-driven. Call setProduct() or setMode() to change state, listen for mode-change or price-change to update your UI. No polling, no manual re-renders.
Core
whim.setProduct(name)
Sets the active product for price lookups and checkout URLs. If you registered a product map, you can pass your own ID here.
whim.setProduct('wireless-headphones-black');
// Or with a mapped ID
whim.map({ 'sku-123456': 'wireless-headphones-black' });
whim.setProduct('sku-123456');
Emits product-change with the resolved WhimProductConfig and price-change with the resolved WhimPrice. If the product name doesn't match anything in your catalog, emits error with an Error describing the failure and leaves the active product unchanged.
whim.getProducts()
Resolves with the full product catalog for your merchant.
var products = await whim.getProducts();
// [{ productName: 'wireless-headphones-black', isOutOfStock: false, price: { amount: 2900, ... }, discountPercent: 20 }, ...]
Returns Promise<WhimProductConfig[]>
whim.getMode()
Resolves with the current mode, either 'buy' or 'whim'. Defaults to 'buy'.
var mode = await whim.getMode(); // 'buy' | 'whim'
Returns Promise<'buy' | 'whim'>
whim.setMode(mode)
Switches between buy and whim. Emits mode-change.
whim.setMode('whim');
whim.isReady
A synchronous read-only boolean. The bootstrap snippet seeds it as false the moment it runs, and the SDK flips it to true once the bundle has loaded and the API is callable. Use it to branch without registering a callback or awaiting anything.
if (whim.isReady) {
renderPrice();
} else {
whim.ui.ready(renderPrice);
}
boolean (property — not a method, do not call or await)
whim.getPrice()
Resolves with the Whim price for the current product, or null before setProduct is called. The SDK only provides Whim pricing — buy pricing is yours.
var price = await whim.getPrice();
// {
// price: 60, // ongoing monthly price
// priceFormatted: '$60',
// currency: 'USD',
// introDiscount: { // null if no discount
// price: 30,
// priceFormatted: '$30',
// percent: 50,
// months: 3
// }
// }
if (price.introDiscount) {
// "$30/mo for 3 months, then $60/mo"
el.textContent = price.introDiscount.priceFormatted + '/mo for '
+ price.introDiscount.months + ' months, then '
+ price.priceFormatted + '/mo';
} else {
el.textContent = price.priceFormatted + '/mo';
}
Returns Promise<WhimPrice | null>
introDiscount is null when the product has no intro discount.
whim.getCheckoutUrl()
Resolves with the Whim checkout URL for the current product and addons, or null before setProduct is called.
var url = await whim.getCheckoutUrl();
// 'https://checkout.fragile.co/demo/checkout?products=wireless-headphones-black'
Returns Promise<string | null>
whim.getConfig()
Resolves with the raw merchant config object.
var config = await whim.getConfig();
// { merchantId: 'demo', currency: 'USD', products: [...] }
Returns Promise<WhimMerchantConfig>
whim.getPortalUrl()
Resolves with the customer portal URL where subscribers manage their subscriptions.
var url = await whim.getPortalUrl(); // 'https://my.whim.com'
Returns Promise<string>
whim.checkCustomer(identifier)
Checks if a customer with that email or phone number already has a Whim account. Returns a promise. You can use this to conditionally render a portal link for returning customers that are already logged in to your site.
Pass exactly one identifier — either { email } or { phone }. To check a customer who may have signed up with either, call it once per identifier.
// By email
var result = await whim.checkCustomer({ email: 'user@example.com' });
// By phone — E.164 with country code
var result = await whim.checkCustomer({ phone: '+15551234567' });
if (result.exists) {
// Returning subscriber, show portal link
window.location.href = whim.getPortalUrl();
}
+15551234567. Matching is exact on the stored number, so any formatting characters (spaces, dashes, parentheses) will cause a miss. A bare national number like 5551234567 also matches US accounts, but E.164 is unambiguous across regions and always recommended.
Returns Promise<{ exists: boolean }>
whim.map(record)
Registers a mapping from your product identifiers to Whim product names. After calling this, you can use your own IDs in setProduct, cart.add, cart.remove, cart.has, etc.
whim.map({
'sku-123456': 'wireless-headphones-black',
'sku-789012': 'wireless-headphones-silver',
});
whim.setProduct('sku-123456'); // resolves to 'wireless-headphones-black'
whim.cart.add('sku-789012'); // resolves to 'wireless-headphones-silver'
Can be called multiple times. New entries merge with previous ones. Unmapped IDs pass through as-is, so you can mix Whim product names and your own.
whim.setAddon(key, value)
Adds an addon to the current product. Addons are included in the checkout URL.
whim.setAddon('case', 'hard-shell');
whim.setAddon('warranty', 'extended');
// checkout URL: ...?products=product-name(addons(case=hard-shell,warranty=extended))
Emits addon-change.
whim.removeAddon(key)
Removes a previously set addon. Emits addon-change.
whim.removeAddon('case');
UI Helpers
whim.ui.ready(fn)
Calls fn immediately if the SDK is loaded, or queues it until it is. Use it to wrap any code that depends on the SDK API being callable.
whim.ui.ready(async function() {
var products = await whim.getProducts();
console.log('SDK ready, products:', products.length);
});
await getter methods directly instead of wrapping in ui.ready(). ui.ready is still useful for callback-style code or when you need to run a block of setup logic together.
whim.ui.refresh()
Triggers a refresh event, causing all <whim-price> and <whim-checkout-button> components to re-render. Call this after client-side page navigation in an SPA.
whim.ui.refresh();
Events
whim.on(event, fn)
Subscribes to an SDK event. Returns a synchronous unsubscribe function — do not await this call.
var off = whim.on('mode-change', function(mode) {
console.log('Mode is now:', mode);
});
// Later
off();
Returns () => void (a sync unsubscribe handle)
Events are also dispatched as DOM CustomEvents on document, prefixed with whim-:
document.addEventListener('whim-mode-change', function(e) {
console.log('Mode is now:', e.detail);
});
whim.on() returns an unsubscribe function. Call it to stop listening, which is useful for cleanup in SPAs or React effects.
Event types
| Event | Payload | When |
|---|---|---|
ready | { price: WhimPrice | null, checkoutUrl: string | null, product: WhimProductConfig | null } | Once after the SDK bundle has loaded and the API is callable. Payload fields are populated when a product is active and null otherwise. |
mode-change | 'buy' | 'whim' | After setMode |
price-change | WhimPrice | After setProduct resolves pricing |
product-change | WhimProductConfig | Every time setProduct() successfully resolves a product, including the first call. |
variant-change | Record<string, string> | After setVariant updates |
addon-change | Record<string, string> | After setAddon or removeAddon |
error | Error | When setProduct can't find a product |
refresh | none | After ui.refresh() |
whim-added-to-cart
A DOM CustomEvent dispatched on document immediately after a successful cart.add call. The detail is the resolved Whim product name. Useful for one-shot UI like toasts or animations that should only fire on a real add.
document.addEventListener('whim-added-to-cart', function(e) {
showToast(e.detail + ' added to cart');
});
Not fired when cart.add is a no-op (item already in the cart) or when the quantity is updated via setQuantity. For ongoing cart state, listen to cart-change instead.
Cart
The cart persists items in localStorage per merchant. All cart methods resolve through whim.map() if you've registered product mappings.
whim.getCheckoutUrl() instead.
whim.cart.add(productName, opts?)
Adds a product to the cart. If the product is already in the cart, its quantity is incremented by opts.quantity (default 1). Resolves to true when the cart actually changed and false when the call was suppressed by debouncing. Emits cart-change, and dispatches whim-added-to-cart on document when the call resolves to true.
whim.cart.add('wireless-headphones-black');
// Add multiple at once
whim.cart.add('wireless-headphones-black', { quantity: 3 });
// With options
whim.cart.add('wireless-headphones-black', {
quantity: 2,
merchantData: { source: 'pdp', listingId: 'abc123' },
addons: { case: 'hard-shell' },
});
// Branch on whether the cart actually changed
if (await whim.cart.add('wireless-headphones-black')) {
// Item added — show success UI
}
Returns Promise<boolean>
Rapid repeated calls for the same product within 500ms are coalesced into a single add. This prevents accidental double-clicks on a buy button from adding the item twice. Calls suppressed this way resolve to false and do not emit events.
whim.cart.setQuantity(productName, quantity)
Sets the absolute quantity for an item already in the cart. Passing 0 removes the item. No-op if the product isn't in the cart. Emits cart-change.
whim.cart.setQuantity('wireless-headphones-black', 3);
whim.cart.setQuantity('wireless-headphones-black', 0); // removes
whim.cart.remove(productName)
Removes a product from the cart. Emits cart-change.
whim.cart.remove('wireless-headphones-black');
whim.cart.has(productName)
Resolves to true if the product is in the cart.
if (await whim.cart.has('wireless-headphones-black')) {
// already in cart
}
Returns Promise<boolean>
whim.cart.get()
Resolves with all cart items with resolved prices.
var items = await whim.cart.get();
// [{
// productName: 'wireless-headphones-black',
// merchantProductId: 'sku-123456', // present if added via mapped ID
// price: { price: 29, introDiscount: { price: 15, months: 3 }, ... },
// merchantData: { source: 'pdp' },
// addons: { case: 'hard-shell' }
// }]
Returns Promise<WhimCartItem[]>
whim.cart.getCheckoutUrl()
Resolves with a single checkout URL for all items in the cart, or null if the cart is empty.
var url = await whim.cart.getCheckoutUrl();
// 'https://checkout.fragile.co/demo/checkout?products=product-a,product-b(addons(case=hard-shell))'
Returns Promise<string | null>
whim.cart.count()
Resolves with the total number of units in the cart, summed across quantities. A cart with two distinct products at quantities 2 and 3 resolves to 5. Use this for cart badges.
Returns Promise<number>
whim.cart.distinctCount()
Resolves with the number of distinct line items in the cart, regardless of quantity. A cart with two distinct products at quantities 2 and 3 resolves to 2.
Returns Promise<number>
whim.cart.clear()
Removes all items from the cart. Emits cart-change.
whim.cart.getProductNames()
Resolves with an array of Whim product names currently in the cart.
Returns Promise<string[]>
whim.cart.on('cart-change', fn)
Subscribes to cart changes. The callback receives a WhimCartChangeEvent with the current cart state. Returns a synchronous unsubscribe function — do not await this call.
var off = whim.cart.on('cart-change', function(e) {
// e = { count, distinctCount, items, subtotal }
updateCartBadge(e.count);
});
Returns () => void (a sync unsubscribe handle)
Types
WhimPrice
{
price: number; // ongoing monthly price
priceFormatted: string; // e.g. '$60'
currency: string; // e.g. 'USD'
introDiscount: { // null if no discount
price: number; // discounted price
priceFormatted: string; // e.g. '$30'
percent: number; // e.g. 50
months: number; // how many months the discount applies
} | null;
}
WhimProductConfig
{
productName: string;
price: {
amount: number; // monthly price in cents
currencyCode: string;
recurringInterval: string;
recurringCount: number;
};
discountPercent: number | null;
}
WhimCartItem
{
productName: string;
merchantProductId?: string; // your ID, if added via map()
quantity: number; // always >= 1
price: WhimPrice;
lineSubtotal: number; // price.price * quantity
merchantData?: Record<string, string>;
addons?: Record<string, string>;
}
WhimCartAddOpts
{
quantity?: number; // default 1
merchantData?: Record<string, string>;
addons?: Record<string, string>;
}
WhimCartChangeEvent
{
count: number; // total units, summed across quantities
distinctCount: number; // number of distinct line items
items: WhimCartItem[];
subtotal: number; // sum of lineSubtotal across all items
}
Components
Web Components for common UI patterns: buy/whim toggles, price display, checkout buttons, and variant switching. Registered automatically when the SDK loads. They react to state changes without additional JS.
Components render in light DOM, so your existing CSS applies. They handle mode switching, price rendering, checkout redirects, and accessibility (role, aria-checked, keyboard support). Each one uses the same JS API under the hood.
You can use components on their own, use the JS API on its own, or combine both. For example, you might use <whim-price> to display the Whim price but handle mode switching and checkout with your own JS. Components and the API share the same state, so they stay in sync automatically.
Styling by mode with data-whim-mode
The following components mirror the active SDK mode onto themselves as a data-whim-mode attribute: <whim-selector>, <whim-price>, <whim-when>, <whim-checkout-button>, and <whim-cart-add>. The attribute value is either "buy" or "whim", and updates synchronously on every mode change. Use it to drive mode-specific styling directly from CSS, without subscribing to events or writing any JS:
/* Emphasize the hero price when the customer is in rent mode */
whim-price[data-whim-mode="whim"] { color: #ea4f1f; }
/* Tint the toggle row background in rent mode */
whim-selector[data-whim-mode="whim"] { background: #fff8f5; }
/* Different button look when wrapped by <whim-cart-add> in rent mode */
whim-cart-add[data-whim-mode="whim"] button { border-color: #ea4f1f; }
Transitions and animations defined in CSS work out of the box.
<whim-selector>
Sets the active product for the page. Wrap your buy/whim toggle in this element.
<whim-selector product="wireless-headphones-black">
<whim-option mode="buy">Buy - $249</whim-option>
<whim-option mode="whim">Rent - <whim-price suffix="/mo" /></whim-option>
</whim-selector>
Attributes
| Attribute | Required | Description |
|---|---|---|
product | Yes | The Whim product name (or your mapped ID if you've called whim.map()) |
The selector calls whim.setProduct() when it connects, so price data and checkout URLs are available immediately after this element mounts.
The current SDK mode is mirrored onto the element as data-whim-mode="buy" or data-whim-mode="whim" so you can style the toggle row from CSS. See Styling by mode for examples.
If the product is out of stock, the element gets a data-out-of-stock attribute. You can use this to hide or restyle the rental option:
whim-selector[data-out-of-stock] { display: none; }
<whim-option>
A buy/whim toggle option. Must be inside a <whim-selector>. Clicking sets the SDK mode and highlights the selected option.
<whim-option mode="buy">Buy</whim-option>
<whim-option mode="whim">Rent</whim-option>
Attributes
| Attribute | Required | Description |
|---|---|---|
mode | Yes | "buy" or "whim" |
default-style | No | Apply the built-in option styles (border, padding, selection state). Omit to fully control the appearance with your own CSS. |
theme-border | No | Border color (default: #e0e0e0). Requires default-style. |
theme-border-selected | No | Selected border color (default: #1a1a1a). Requires default-style. |
theme-border-radius | No | Border radius (default: 10px). Requires default-style. |
theme-padding | No | Padding (default: 16px). Requires default-style. |
The selected option gets the .whim-selected class and aria-checked="true". Keyboard accessible (Enter and Space).
Styling
By default, <whim-option> ships unstyled so it inherits cleanly from your existing layout. Add the default-style attribute to opt into the built-in look:
<whim-option mode="buy" default-style>Buy</whim-option>
<whim-option mode="whim" default-style>Rent</whim-option>
With default-style, you can theme the option through CSS custom properties:
whim-option {
--whim-border: #ccc;
--whim-border-selected: #0066ff;
--whim-border-radius: 8px;
--whim-padding: 12px 16px;
}
Or through theme-* attributes directly on the element:
<whim-option mode="whim" default-style theme-border="#ddd" theme-border-selected="blue">
Rent
</whim-option>
.whim-selected class on the active option lets you style the selected state however you want.
<whim-price>
Renders Whim pricing for the current product. In buy mode, shows whatever you put in buy-price (or nothing). Re-renders automatically on mode and product changes.
<whim-price buy-price="$249" suffix="/mo" />
<!-- No discount: "$60/mo" -->
<!-- With discount: "$30/mo first month, then $60/mo" -->
If the product has an intro discount, the component automatically renders the full pricing copy (e.g. "$30/mo first month, then $60/mo") so the ongoing price is always visible.
Attributes
| Attribute | Required | Description |
|---|---|---|
buy-price | No | Text to show when mode is "buy". If omitted, shows nothing in buy mode. |
show | No | "full" to render just the ongoing monthly price (no discount copy). "first" to render only the discounted price when an intro discount applies, or the ongoing price otherwise. Useful if you want to compose your own layout. |
prefix | No | Text before the price (e.g. "From ") |
suffix | No | Text after the price (e.g. "/mo") |
out-of-stock-label | No | Text to show when the product is out of stock in whim mode. Defaults to "Out of Stock". |
<whim-price> automatically renders the full discount copy (e.g. "$30/mo first 3 months, then $60/mo"). Use show="full" if you only want the ongoing monthly price.
The current SDK mode is mirrored onto the element as data-whim-mode="buy" or data-whim-mode="whim" so you can recolor or restyle the price by mode from CSS. See Styling by mode for examples.
<whim-teaser-price>
Teaser for listing pages and product grids. Renders a one-liner like "or rent for $276/mo" next to the buy price. The program name (rent, try, subscribe) comes from your merchant config. Shows the intro discount price when available.
<p class="buy-price">$3,054.00</p>
<whim-teaser-price product="leica-q2" />
<!-- renders: "or rent for $276/mo" -->
Attributes
| Attribute | Required | Description |
|---|---|---|
product | Yes | The Whim product name (or your mapped ID) |
Styling
The element is display: inline by default, so it inherits styles from its parent. Style it like any other element:
whim-teaser-price {
color: #e85d3a;
font-size: 14px;
display: block;
}
Listing page example
Drop one into each product card on a category page:
<div class="product-card">
<img src="/leica-q2.jpg" alt="Leica Q2" />
<h3>Leica Q2</h3>
<p class="price">$3,054.00 - $3,943.00</p>
<whim-teaser-price product="leica-q2" />
</div>
<whim-price> (which uses the active product from <whim-selector>), each <whim-teaser-price> resolves its own product independently via the product attribute. This makes it ideal for pages showing many products at once.
<whim-checkout-button>
Wraps your existing checkout button or link. In whim mode, it redirects to the Whim checkout URL. In buy mode, your existing checkout still works as-is.
With a link
The component swaps the href when mode is "whim" and restores it when mode is "buy":
<whim-checkout-button>
<a href="/cart/add?sku=sku-123456">Add to Cart</a>
</whim-checkout-button>
With a button
The component intercepts clicks in whim mode and redirects to checkout. In buy mode, the click passes through to your handler:
<whim-checkout-button>
<button onclick="addToCart()">Add to Cart</button>
</whim-checkout-button>
The component detects whether the child is an <a> or a <button> and behaves accordingly.
Attributes
| Attribute | Required | Description |
|---|---|---|
buy-label | No | Text shown on the inner button or link when mode is "buy". The original text is captured on first render and restored when this attribute is omitted for the active mode. |
whim-label | No | Text shown on the inner button or link when mode is "whim". |
<whim-checkout-button buy-label="Add to Cart" whim-label="Start Rental">
<button>Add to Cart</button>
</whim-checkout-button>
The current SDK mode is mirrored onto the wrapper as data-whim-mode="buy" or data-whim-mode="whim" so you can restyle the inner button or link by mode from CSS. See Styling by mode for examples.
When the product is out of stock, the inner trigger is automatically disabled (disabled + aria-disabled="true") and clicks are blocked. The wrapper also gets a data-out-of-stock attribute for CSS styling.
<whim-cart-add>
Wraps your existing add-to-cart button. In whim mode, it adds the product to the Whim cart and dispatches a whim-added-to-cart event. In buy mode, the click passes through to your handler.
<whim-cart-add
product="wireless-headphones-black"
buy-label="Add to Cart"
whim-label="Add to Rental Cart"
>
<button onclick="addToCart()">Add to Cart</button>
</whim-cart-add>
Attributes
| Attribute | Required | Description |
|---|---|---|
product | Yes | The Whim product name (or your mapped ID) to add when clicked in whim mode. |
quantity | No | How many units to add per click (default: 1). If the product is already in the cart, this is added to the existing quantity. |
buy-label | No | Text shown on the inner button when mode is "buy". The original text is captured on first render and restored when this attribute is omitted for the active mode. |
whim-label | No | Text shown on the inner button when mode is "whim". |
The whim-added-to-cart event
When a click in whim mode results in a successful add, the component dispatches a bubbling whim-added-to-cart CustomEvent. The event only fires when the cart was actually mutated, so a toast hooked to it never shows a false confirmation:
document.addEventListener('whim-added-to-cart', function (e) {
// e.detail = { product, quantity }
showToast('Added ' + e.detail.product + ' to your rental cart');
});
The current SDK mode is mirrored onto the wrapper as data-whim-mode="buy" or data-whim-mode="whim" so you can restyle the inner add-to-cart button by mode from CSS. See Styling by mode for examples.
Out-of-stock products are blocked from being added to the cart. The click is silently swallowed.
<whim-when>
Shows or hides its children based on the current SDK mode. Use it to swap product page sections (gear grade tables, warranty options, rental perks) without writing any JavaScript.
<whim-when mode="buy">
<!-- Visible only in buy mode -->
<p>Free shipping on orders over $75</p>
</whim-when>
<whim-when mode="whim">
<!-- Visible only in whim mode -->
<p>Cancel anytime. No long-term commitment.</p>
</whim-when>
Attributes
| Attribute | Required | Description |
|---|---|---|
mode | Yes | "buy", "whim", or a comma-separated list (e.g. "buy,whim") to match multiple modes. |
When the mode doesn't match, the element is hidden with display: none and takes no layout space.
The current SDK mode is also mirrored onto the element as data-whim-mode="buy" or data-whim-mode="whim", regardless of whether this <whim-when> currently matches, so you can target descendants from CSS based on the active mode (e.g. animate a card on entry). See Styling by mode for examples.
<whim-variant> / <whim-variant-option>
Handles variant switching (color, condition, size, etc.) by wrapping your existing variant buttons. When a customer clicks a variant option, the SDK switches to the corresponding product.
<whim-variant name="color">
<whim-variant-option product="wireless-headphones-black">
<button>Black</button>
</whim-variant-option>
<whim-variant-option product="wireless-headphones-silver">
<button>Silver</button>
</whim-variant-option>
<whim-variant-option product="wireless-headphones-navy">
<button>Navy</button>
</whim-variant-option>
</whim-variant>
<whim-variant> attributes
| Attribute | Required | Description |
|---|---|---|
name | Yes | The variant attribute name (e.g. "color", "size") |
<whim-variant-option> attributes
| Attribute | Required | Description |
|---|---|---|
product | Yes* | The Whim product name for this variant |
value | No | Reserved for future configurable product support |
*Use product in v1. Each variant option maps to a distinct product name in your catalog.
How it works
Each <whim-variant-option> wraps a single child element (your existing button or swatch). The SDK attaches a click listener that calls whim.setProduct() with the specified product name. This triggers ready and price-change events, so <whim-price> and <whim-checkout-button> update automatically.
setProduct() internally, which re-resolves pricing for the new product. Don't cache prices between variants yourself.
Full example
A complete product page with buy/whim toggle, variant switching, price display, and checkout:
<whim-selector product="wireless-headphones-black">
<!-- Buy/rent toggle -->
<whim-option mode="buy">
Buy - $249
</whim-option>
<whim-option mode="whim">
Rent - <whim-price suffix="/mo" />
</whim-option>
<!-- Color variants -->
<whim-variant name="color">
<whim-variant-option product="wireless-headphones-black">
<button>Black</button>
</whim-variant-option>
<whim-variant-option product="wireless-headphones-silver">
<button>Silver</button>
</whim-variant-option>
</whim-variant>
<!-- Checkout -->
<whim-checkout-button>
<a href="/cart/add?sku=sku-123456">Add to Cart</a>
</whim-checkout-button>
</whim-selector>
The SDK manages all the state: mode switching, price updates, checkout URLs. The HTML and CSS are yours.
Product explorer
Enter your merchant ID to pull your product catalog from the Whim API. This is the same data the SDK uses at runtime.
Sandbox
A mock product page with buy/whim toggle, pricing, and checkout URL. Load your products above to populate it with real data.
The buy price