Whim SDK

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+.

Tip: You can call SDK methods before the script loads. The snippet queues everything and replays it on init.
  • Getters and cart data methods return promises. Use await or .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() and whim.cart.on() return an unsubscribe function (sync, don't await).
  • whim.isReady is a boolean. Starts false when the snippet runs, becomes true once 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.

Tip: These components wrap your existing HTML. Your product title, images, and layout stay as-is. You just add the Whim elements around the parts that need buy/whim behavior.
1

Set the product

<whim-selector> tells the SDK which product to load. Everything else goes inside it.

2

Add the buy/whim toggle

<whim-option> lets shoppers switch between buy and whim mode. The SDK handles the state, selection styling, and accessibility.

3

Display the price

<whim-price> shows the Whim price in whim mode. Set buy-price to show your buy price when in buy mode.

4

Handle checkout

<whim-checkout-button> wraps your existing CTA. Redirects to Whim checkout in whim mode.

Preview
Wireless Headphones
Black
$249
<!-- 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.

Tip: When you switch modes, all price displays and checkout buttons update automatically. You don't need to manually re-render anything after calling 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.

Tip: If your site has variant selectors (color, size, etc.), hook into those change events and call 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.

Tip: The JS API is always available, even if you're using components. Call 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();
}
Phone format: Pass phone numbers in E.164 format with the country code — e.g. +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);
});
Tip: If you're using the bootstrap queue, you can also just 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);
});
Tip: whim.on() returns an unsubscribe function. Call it to stop listening, which is useful for cleanup in SPAs or React effects.

Event types

EventPayloadWhen
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-changeWhimPriceAfter setProduct resolves pricing
product-changeWhimProductConfigEvery time setProduct() successfully resolves a product, including the first call.
variant-changeRecord<string, string>After setVariant updates
addon-changeRecord<string, string>After setAddon or removeAddon
errorErrorWhen setProduct can't find a product
refreshnoneAfter 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.

Tip: The cart is for multi-product Whim flows where shoppers collect items across PDPs before checking out. If you only need single-product checkout, use whim.getCheckoutUrl() instead.
Products
Wireless Headphones
Black, Noise Cancelling
$29/mo
Portable Speaker
Waterproof, 20W
$15/mo
Smart Watch
Silver, GPS
$35/mo
Rental
Console

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

AttributeRequiredDescription
productYesThe 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

AttributeRequiredDescription
modeYes"buy" or "whim"
default-styleNoApply the built-in option styles (border, padding, selection state). Omit to fully control the appearance with your own CSS.
theme-borderNoBorder color (default: #e0e0e0). Requires default-style.
theme-border-selectedNoSelected border color (default: #1a1a1a). Requires default-style.
theme-border-radiusNoBorder radius (default: 10px). Requires default-style.
theme-paddingNoPadding (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>
Tip: Components render in light DOM, so all your existing CSS applies. The .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

AttributeRequiredDescription
buy-priceNoText to show when mode is "buy". If omitted, shows nothing in buy mode.
showNo"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.
prefixNoText before the price (e.g. "From ")
suffixNoText after the price (e.g. "/mo")
out-of-stock-labelNoText to show when the product is out of stock in whim mode. Defaults to "Out of Stock".
Tip: When a product has an intro discount, <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

AttributeRequiredDescription
productYesThe 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>
Tip: Unlike <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

AttributeRequiredDescription
buy-labelNoText 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-labelNoText 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>
Tip: In buy mode, the checkout button does nothing. Your existing checkout behavior runs as-is, no conditional logic needed.

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

AttributeRequiredDescription
productYesThe Whim product name (or your mapped ID) to add when clicked in whim mode.
quantityNoHow many units to add per click (default: 1). If the product is already in the cart, this is added to the existing quantity.
buy-labelNoText 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-labelNoText 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');
});
Tip: Rapid repeated clicks on the same product within a short window are coalesced into a single add. The event fires once per real add, so you can wire a toast directly to it without debouncing yourself.

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

AttributeRequiredDescription
modeYes"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

AttributeRequiredDescription
nameYesThe variant attribute name (e.g. "color", "size")

<whim-variant-option> attributes

AttributeRequiredDescription
productYes*The Whim product name for this variant
valueNoReserved 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.

Tip: Variant switching calls 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.

Enter your merchant ID and click Load Products.

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