Product bundling is one of those features that sounds simple until you actually try to implement it in a headless commerce setup. Most out-of-the-box platforms handle bundles through rigid plugin configurations. With Medusa.js, you have the flexibility to build bundling logic that fits exactly how your business thinks about products, whether that is kit bundles, promotional combos, or subscription starter packs.
This guide walks through the approaches available in Medusa.js for building product bundles and combo offers, how to architect the data model, and what to keep in mind when surfacing bundles on your storefront.
What Is a Product Bundle in Headless Commerce?
In traditional e-commerce platforms, a bundle is usually a fixed plugin feature where you select products and define a discount. In headless commerce, a bundle is more accurately a presentation and pricing concept that you model yourself using the platform's data primitives. Medusa.js does not ship a native bundle type, but it gives you the building blocks to create one cleanly.
There are broadly three patterns teams use when building bundles in Medusa.js:
Variant-level bundles: A bundle is created as a product with variants that represent different bundle configurations
Metadata bundles: A product carries metadata that references other product IDs, and the storefront renders them as a group
Custom entity bundles: A separate bundle entity is created in the Medusa module system that owns its own pricing, images, and product associations
Each approach has trade-offs in terms of pricing flexibility, inventory tracking, and storefront rendering complexity. The right choice depends on whether your bundles need independent inventory, whether pricing is fixed or dynamic, and how deeply bundles need to integrate with your order logic.
Building bundles on Medusa.js?
Let's TalkApproach 1: Using Product Variants for Simple Combos
For straightforward combo offers where a fixed set of products is sold together at a set price, modelling the bundle as a Medusa product with variants is the fastest path. You create a product called, say, "Starter Kit" and add variants for different configurations. Each variant carries its own price and can be linked to a custom metadata field that lists the component product IDs.
The storefront then reads the metadata and renders the individual product cards within the bundle UI. Inventory for the bundle is tracked separately from the component products unless you write custom logic to decrement them together on order creation. This is acceptable for many brands that manage bundle stock independently.
A basic product creation with bundle metadata would look like this in your Medusa storefront API call:
const bundle = await medusa.products.create({
title: 'Summer Starter Kit',
metadata: {
is_bundle: true,
component_ids: ['prod_abc123', 'prod_def456'],
},
variants: [{ title: 'Default', prices: [{ amount: 1999, currency_code: 'inr' }] }]
});Approach 2: Custom Bundle Module in Medusa.js 2.0
Medusa.js 2.0 introduced the module system, which lets you create entirely custom data models that integrate cleanly with the rest of the commerce logic. Building a bundle module gives you a dedicated entity, its own service layer, and the ability to hook into Medusa's event bus for order processing.
This is the recommended approach for stores where bundles are a core feature rather than an afterthought. With a custom module, you can:
Define bundle pricing rules independently of individual product pricing
Track which bundles contain which products with a proper relational table
Use Medusa workflows to handle bundle fulfilment, where component items are picked and packed together
Expose a clean REST endpoint for your storefront to fetch bundle details with component enrichment
Building the Storefront Bundle UI
On the storefront side, a bundle product page needs to render the component products in a way that communicates value clearly without creating confusion about what is being purchased. A common pattern is to show each component product as a card within a bundle section, with the individual retail price crossed out and the bundle price shown prominently.
When building with Next.js and the Medusa.js storefront starter, you can fetch bundle metadata within the product page loader and enrich the component IDs with full product data using a parallel fetch. This keeps bundle pages performant because you are not nesting API calls in the render path.
For reference on building the Medusa storefront with Next.js, the
Pricing Logic for Combo Offers
One of the advantages of building bundles in Medusa.js is the ability to implement pricing logic that would be difficult in monolithic platforms. Combo offer pricing can be handled in a few different ways.
Fixed bundle price is the simplest: the bundle variant has a set price that the customer sees. No calculation happens at checkout beyond applying the bundle price. This works well for curated kits with a clear value proposition.
Percentage discount bundles are handled at the checkout level using Medusa's discount engine. You create a discount rule that applies when a specific product combination is in the cart. The order total reflects the reduction. This approach does not require a separate bundle entity and works naturally with Medusa's existing cart and checkout flow.
Dynamic bundle pricing, where the final price depends on which component variants the customer selects, requires custom logic in the Medusa workflow layer. This is the most complex but most flexible approach, suitable for brands that offer configure-to-order bundles.
Inventory Considerations for Bundles
Inventory is where bundle complexity tends to surface most aggressively. If a bundle contains products that are also sold individually, you need a strategy for how selling the bundle decrements the inventory of its components. Medusa.js does not handle this automatically unless you build it.
The cleanest approach is to write a Medusa subscriber that listens to the order.placed event and, when a bundle product is included in the order, programmatically decrements the inventory of each component variant using the InventoryService. This keeps your component inventory accurate and prevents overselling.
For stores where bundle components are never sold independently, tracking inventory only at the bundle level is simpler and perfectly valid. The decision comes down to how your fulfilment operates.
Testing Your Bundle Implementation
Before pushing bundle functionality to production, walk through the full purchase journey including edge cases. Test what happens when a bundle is partially out of stock. Verify that the checkout summary reflects the bundle price correctly rather than the sum of component prices. Confirm that order confirmation emails show the bundle as a single line item rather than exploding into individual products, which confuses customers.
Teams that have implemented Medusa.js in production through
Bundles done well in Medusa.js deliver a noticeably better buying experience. When customers see clearly what they are getting, at a price that makes sense, and the checkout handles it cleanly, conversion on bundle pages tends to be meaningfully higher than on individual product pages. The investment in getting the architecture right at the start pays off well beyond the first feature launch.
Need help with Medusa.js pricing logic?
Let's TalkWritten by
Kannan Rajendiran
CEO
