Skip to main content

Add Bundle to Cart

Learn how to add bundles to cart, handle configuration for dynamic bundles, and manage common errors.

Goal

Successfully add both fixed and dynamic bundles to cart with proper validation and user feedback.

Prerequisites

  • Bundle product data with components
  • Active cart session
  • User selections for dynamic bundles

Quick Start

import { 
client,
configureByContextProduct,
manageCarts,
getCartId,
getAllFiles
} from '@epcc-sdk/sdks-shopper';

// Configure client
client.setConfig({
baseUrl: 'https://euwest.api.elasticpath.com',
headers: { Authorization: `Bearer ${ACCESS_TOKEN}` },
});

// Add bundle to cart (automatically detects type)
async function addBundleToCart(
bundle: BundleProduct,
selectedOptions?: BundleConfiguration["selected_options"],
quantity: number = 1
) {
const cartId = getCartId();

// Detect if bundle is dynamic based on min/max requirements
const isDynamic = bundle.attributes?.components &&
Object.values(bundle.attributes.components).some(
comp => comp.min != null || comp.max != null
);

if (isDynamic && selectedOptions) {
// Dynamic bundles with user selections need configuration
const config = await configureByContextProduct({
path: { product_id: bundle.id },
body: { data: { selected_options: selectedOptions } }
});

// Add with configuration
return await manageCarts({
path: { cartID: cartId },
body: {
data: {
type: 'cart_item',
id: bundle.id,
quantity,
bundle_configuration: config.data?.meta?.bundle_configuration
}
}
});
} else {
// Fixed bundles or dynamic bundles without selections
// API will use default configuration automatically
return await manageCarts({
path: { cartID: cartId },
body: {
data: {
type: 'cart_item',
id: bundle.id,
quantity
}
}
});
}
}
Bundle Configuration
  • Dynamic bundles with user selections: Require bundle_configuration property
  • Fixed bundles: Can be added without configuration - the API uses default configuration automatically
  • Dynamic bundles without selections: Will use minimum required selections as defaults

Key Concepts

Validate Bundle Configuration

Ensure dynamic bundle selections meet component requirements:

interface ValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
}

function validateBundleConfiguration(
components: Components,
selectedOptions: BundleConfiguration["selected_options"]
): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];

// Check each component's requirements
Object.entries(components).forEach(([componentKey, component]) => {
const selected = selectedOptions[componentKey] || {};
const selectedCount = Object.values(selected)
.reduce((sum, qty) => sum + Number(qty), 0);

// Validate minimum requirement
if (component.min && selectedCount < component.min) {
errors.push(
`${component.name}: Please select at least ${component.min} option(s)`
);
}

// Validate maximum requirement
if (component.max && selectedCount > component.max) {
errors.push(
`${component.name}: Maximum ${component.max} option(s) allowed`
);
}

// Warn about optional components
if (!component.min && selectedCount === 0) {
warnings.push(
`${component.name}: No selection made (optional)`
);
}
});

return {
isValid: errors.length === 0,
errors,
warnings
};
}

Auto-Replacement for Maximum Selections

Provide better UX by automatically replacing the oldest selection when maximum is reached:

function handleComponentSelection(
componentKey: string,
optionId: string,
component: ComponentProduct,
currentSelections: BundleConfiguration["selected_options"]
): BundleConfiguration["selected_options"] {
const componentSelections = currentSelections[componentKey] || {};
const selectedCount = Object.keys(componentSelections).length;

if (component.max && selectedCount >= component.max) {
// Auto-replace oldest selection
const updated = { ...componentSelections };
const oldestKey = Object.keys(updated)[0];
delete updated[oldestKey];
updated[optionId] = 1;

return {
...currentSelections,
[componentKey]: updated
};
}

// Normal addition
return {
...currentSelections,
[componentKey]: {
...componentSelections,
[optionId]: 1
}
};
}

Fetching Component Product Images

When displaying bundle components with images, use the efficient batch fetching pattern described in Get Bundle - Component Product Images.

Key Points
  • Component images are not included in the bundle response
  • Use getAllFiles with an in() filter to fetch all images in one API call
  • This approach significantly improves performance for bundles with many components

Best Practices

  • Validate dynamic bundle configurations before adding to cart
  • Show loading states during async operations
  • Handle errors with user-friendly messages
  • Fetch component product images efficiently using batch operations

Common Issues & Solutions

"Bundle configuration is required" Error

  • Cause: Attempting to add a dynamic bundle with custom selections but without configuration
  • Solution: For dynamic bundles with user selections, call configureByContextProduct first and include the bundle_configuration. Fixed bundles don't require configuration

"Invalid bundle configuration" Error

  • Cause: Configuration doesn't meet component requirements
  • Solution: Validate configuration before sending to API

"Insufficient stock" Error

  • Cause: Bundle or components out of stock
  • Solution: Check inventory before add to cart, show availability

Next Steps

References