How to run a 3 for 2 with the cheapest item free on Shopify
Across a mixed collection, which of the three items is free decides whether the promotion costs you a little or a lot. Freeing the cheapest qualifying item is the default because it is the margin-safe choice and the one shoppers already expect.
- Buy X get Y
- $30.00
Scalp Renew Serum
Qty 1
- $28.00
Deep Repair Hair Mask
Qty 1
- $20.00
Volumize Daily Shampoo
Qty 1
Three items go in and the cheapest one drops to zero, with the saving shown on its own line. The behaviour matches what shoppers already assume a 3 for 2 means, so there is nothing to explain.
How to set this up
- 1
Campaign > Offer type
Pick Buy X get Y.
type = bxgy
- 2
Target
Target the collection the promotion covers. Every item in it is both a qualifying item and a possible free item.
target.scope = collections
- 3
Buy and get
Buy quantity 2, get quantity 1, at 100% off.
bxgy.buyQty = 2
- 4
Get selection
Set the get selection to cheapest eligible so the free unit is drawn from the same pool rather than matching the trigger.
bxgy.getSelection = cheapest_eligible
- 5
Allocation
Leave allocation at cheapest first. Switching to most expensive first makes the same promotion considerably more expensive on a mixed cart, so change it only on purpose.
stacking.allocation = cheapest_first
- 6
Set counting
Leave free units as additional so a set is two paid plus one free, which is how a 3 for 2 is normally understood.
bxgy.freeUnitsAreAdditional = true
- 7
Preview
Simulate a cart with a wide price spread and confirm which unit was freed, then publish.
Every setting, spelled out
These are the real configuration keys the offer is saved with, so you can check your own setup against this table field by field.
| Setting | Value | Config key |
|---|---|---|
| Offer type | bxgy | type |
| Buy quantity | 2 | bxgy.buyQty |
| Get quantity | 1 | bxgy.getQty |
| Get selection | cheapest_eligible | bxgy.getSelection |
| Get reward | 100% off | bxgy.getDiscountPct |
| Allocation | cheapest_first (default) | stacking.allocation |
| Set counting | free units additional | bxgy.freeUnitsAreAdditional |
| Reward cap | optional per order | bxgy.maxRewardsPerOrder |
See it built end to end
The episode is not up yet
We are recording this one. The written recipe above is complete, so you can build it today.
What Shopify does natively
Native Buy X Get Y frees the lowest-priced qualifying unit, so the basic behaviour matches without an app.
Native Buy X Get Y gives no control over allocation if you want the opposite behaviour, no independent cap on discounted units per order, and no way to preview a mixed-price cart before publishing.
Watch out for
- Allocation is a margin decision, not a display preference. Most expensive first on a wide price range can cost several times more per order.
- Free units are additional by default, so six items yield two free rather than three.
- Without a cap on rewards per order, a large order can free a lot of units at once.
- If the collection spans a very wide price range, consider narrowing the target rather than accepting the spread.
Common questions
- Which item is free in a 3 for 2?
- The cheapest qualifying item, by default. That matches native Shopify behaviour and shopper expectations.
- Can I make the most expensive item free instead?
- Yes, by changing allocation. Model the cost first, because on a mixed cart the difference is large.
- Why does a cart of six only free two items?
- Because each set is two paid plus one free by default. Six units make two complete sets.
- Can I cap how many free units one order can take?
- Yes, with a per-order cap on rewards.
Related examples
Buy one get one where any variant in the range can be the free unit.
Second item half price, repeating on every subsequent pair rather than once per cart.
Any 3 items from a collection for one price, repeating for every complete set.
Build this in your own store
Every recipe in this library runs on the same server-side engine, so the cart total and the checkout total agree.