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​

Ask External AI