Claude
Skills
Sign in
Back

product-bundles-kits

Included with Lifetime
$97 forever

Sell grouped products as bundles or kits with automatic inventory deduction, bundle pricing, and display logic using platform apps

catalog-inventorybundleskitsproduct-setsdynamic-pricingupsellinventorycross-sell

What this skill does


# Product Bundles & Kits

## Overview

Product bundles let you sell multiple products together — optionally at a discount — as a single purchasable unit. This increases average order value and is one of the most effective upsell mechanisms in e-commerce. Dedicated bundle apps handle the inventory tracking, pricing, and display logic that platforms don't support natively. Only build a custom bundle system if your kit configuration requirements (dynamic assembly, real-time pricing, component substitution) exceed what apps offer.

## When to Use This Skill

- When implementing a "Frequently Bought Together" or "Complete the Look" feature
- When selling product kits (e.g., a camera body + lens + bag as a bundle)
- When offering a discount for purchasing a set of products together
- When building a custom kit builder where shoppers assemble their own set from options

## Core Instructions

### Step 1: Determine platform and choose the right tool

| Platform | Recommended Tool | Why |
|----------|-----------------|-----|
| **Shopify** | Bundler — Product Bundles or Bundle Builder | Bundler is the most popular; handles fixed bundles, mix-and-match, and inventory deduction per component |
| **WooCommerce** | WooCommerce Product Bundles (WooCommerce extension) | Official WooCommerce extension; supports fixed and configurable bundles, per-component pricing, and inventory |
| **BigCommerce** | Product Bundler app or native "Frequently Bought Together" | BigCommerce App Marketplace has several bundle apps; native "Frequently Bought Together" for simple cross-sells |
| **Custom / Headless** | Build bundle data model with component inventory deduction | Required when bundle logic (real-time pricing, dynamic component substitution) exceeds app capabilities |

---

### Step 2: Platform-specific setup

---

#### Shopify

**Option A: Bundler — Product Bundles (recommended)**

1. Install **Bundler — Product Bundles** from the Shopify App Store
2. Go to **Bundler → Bundles → Create bundle**
3. Select bundle type:
   - **Fixed bundle**: a predefined set of products (e.g., "Skincare Starter Kit")
   - **Mix & Match**: shopper picks X items from a collection
   - **Build a Box**: shopper fills a box with any combination
4. Add component products and set quantities
5. Set pricing:
   - **Percentage discount** (e.g., 15% off the combined price)
   - **Fixed bundle price** (e.g., $49.99 regardless of component prices)
   - **Sum of parts** (no discount — just convenience bundling)
6. Bundler handles inventory: when a bundle is purchased, it decrements each component's stock automatically

**For "Frequently Bought Together":**
- Install **Frequently Bought Together** by Code Black Belt (App Store)
- The app analyzes your order history and automatically suggests products to bundle on the PDP
- Works out of the box with no manual configuration

**Important for Shopify Plus:**
- For bundles that must appear as a single line item in the cart (for loyalty points, discount exclusions, etc.), consider **Shopify Bundles** (native, available in admin under **Products → Bundles**)
- Shopify's native Bundles product type creates a parent SKU that the buyer purchases, with automatic inventory deduction of components

---

#### WooCommerce

**WooCommerce Product Bundles (official extension):**

1. Install **WooCommerce Product Bundles** from WooCommerce.com
2. Create a new product: **Products → Add Product → Product type: Bundle**
3. Go to the **Bundled Products** tab
4. Add each component:
   - Search and select the product
   - Set the default quantity and whether it's optional (for configurable bundles)
   - Enable **Ship separately** if components ship from different locations
5. Set bundle pricing under the **General** tab:
   - **Per-item pricing**: bundle price = sum of component prices (with optional discount)
   - **Static bundle pricing**: enter a fixed price for the bundle
6. **Inventory**: enable **Manage stock?** if you want to cap how many bundles can be sold (based on component availability)

**For "Frequently Bought Together":**
- Install **WooCommerce Frequently Bought Together** (free or paid versions available)
- Or use **YITH WooCommerce Frequently Bought Together**

---

#### BigCommerce

**Bundle apps from the App Marketplace:**

1. Search for **"Bundle"** in the **Apps** section of your BigCommerce admin
2. **Bold Bundles** is widely used — install and configure bundle products linking to existing SKUs
3. Set discount rules (percent off, fixed price, BOGO)
4. Bold Bundles handles inventory decrement for each component automatically

**Native cross-sell / "Frequently Bought Together":**
1. Go to **Products → [Product] → Related Products tab**
2. Add related products — these appear as suggestions on the PDP
3. No automatic discount, but customers can add individual items

---

#### Custom / Headless

For headless storefronts, build a bundle model where components are stored as separate cart line items (simplifies inventory, tax, and fulfillment):

```typescript
// lib/bundles.ts

interface BundleComponent { variantId: string; quantity: number; unitPrice: number; }
interface BundlePricing { componentSum: number; bundlePrice: number; savings: number; savingsPct: number; }

// Calculate bundle price dynamically from current component prices
export async function calculateBundlePrice(bundle: Bundle, selectedVariants: BundleComponent[]): Promise<BundlePricing> {
  const componentSum = selectedVariants.reduce((total, c) => total + c.unitPrice * c.quantity, 0);

  let bundlePrice: number;
  switch (bundle.pricingType) {
    case 'fixed': bundlePrice = bundle.pricingValue; break;
    case 'discount_pct': bundlePrice = +(componentSum * (1 - bundle.pricingValue / 100)).toFixed(2); break;
    case 'discount_abs': bundlePrice = Math.max(0, +(componentSum - bundle.pricingValue).toFixed(2)); break;
    default: bundlePrice = componentSum;
  }

  const savings = +(componentSum - bundlePrice).toFixed(2);
  return { componentSum, bundlePrice, savings, savingsPct: componentSum > 0 ? Math.round((savings / componentSum) * 100) : 0 };
}

// Check availability for all bundle components atomically
export async function checkBundleAvailability(selectedVariants: BundleComponent[]) {
  const unavailable = [];
  for (const { variantId, quantity } of selectedVariants) {
    const level = await db.inventoryLevels.findFirst({ where: { variantId } });
    const available = (level?.onHand ?? 0) - (level?.reserved ?? 0);
    if (available < quantity) {
      unavailable.push({ variantId, requested: quantity, available: Math.max(0, available) });
    }
  }
  return { available: unavailable.length === 0, unavailable };
}

// Add bundle to cart as separate line items (grouped by bundle ID for UI display)
export async function addBundleToCart(cartId: string, bundleId: string, selectedVariants: BundleComponent[]) {
  const bundle = await db.productBundles.findUnique({ where: { id: bundleId } });
  const pricing = await calculateBundlePrice(bundle, selectedVariants);

  const { available, unavailable } = await checkBundleAvailability(selectedVariants);
  if (!available) throw new Error(`Bundle not available: ${unavailable.map(u => u.variantId).join(', ')}`);

  // Create a bundle group record to keep line items visually associated
  const bundleGroup = await db.cartBundleGroups.create({
    data: { cartId, bundleId, bundlePrice: pricing.bundlePrice, bundleSavings: pricing.savings },
  });

  // Pro-rate the discount across components proportionally to their share of the total
  const lineItems = selectedVariants.map(({ variantId, quantity, unitPrice }) => {
    const itemSubtotal = unitPrice * quantity;
    const discountShare = pricing.savings * (itemSubtotal / pricing.componentSum);
    const discountedUnitPrice = +(unitPrice - discountShare / quantity).toFixed(4);
    return { cartId, variantId, quantity, bundleGroupId: bundleGroup.id, unitPrice: discountedUnitPrice };
  });

  await db.cartItems.crea

Related in catalog-inventory