how-to · · 7 min read
how to set cost of goods on woocommerce variable products
A variable product doesn't have one cost. It has one per size. Here is how to set cost of goods per variation in WooCommerce, which variations actually need their own number, and the traps that make big sizes look more profitable than they are.
By Big Bad Agency
A simple product has one cost. You bought it for $6, you sell it for $15, and the margin is $9. Done.
A variable product breaks that. A t-shirt in five sizes, a candle in three scents and two sizes, a coffee in 250 g and 1 kg bags. They all share one product page, one title and one set of photos. They don’t share one cost. The 1 kg bag doesn’t cost four times the 250 g bag to produce, and the XXL shirt isn’t the same blank as the small.
When every variation inherits one cost from the parent product, the report is quietly wrong. Here’s how to set it up so it isn’t.
why one cost on the parent lies
Take a made-up example. Plug in your own numbers.
A coffee roaster sells one blend in two bags:
| variation | price | real cost of goods | margin with real cost |
|---|---|---|---|
| 250 g | $16 | $6.50 | 59% |
| 1 kg | $48 | $21.00 | 56% |
Now suppose the cost was typed once on the parent as $6.50, because that’s the bag they launched with, and the 1 kg bag inherits it. The report now says the 1 kg bag costs $6.50 and makes an 86% margin. It looks like the best product in the shop. It’s actually slightly worse than the small bag.
That’s not a rounding error. It’s the kind of number that decides which product gets the ad budget, and it’s wrong in the direction that feels good.
The pattern is always the same: the variation that costs more than the parent’s number looks more profitable than it is. Usually that’s the biggest size, the premium material, or the bundle.
which variations actually need their own cost
You don’t need to type a number into every one of 40 variations. You need a cost wherever the cost is different.
Go through your attributes one at a time and ask one question: does this attribute change what the item costs you?
- Size or weight: almost always yes. More product, bigger blank, more material.
- Pack quantity (1, 3, 6): yes. Cost is the unit cost times the count, plus any packaging difference.
- Material or finish: usually yes. Solid vs plated, cotton vs organic cotton.
- Colour or scent: often no. A red mug and a blue mug from the same supplier usually cost the same.
That gives you a simple setup:
- Put the most common cost on the parent as a default.
- Override only the variations that differ. In the coffee example, that’s just the 1 kg bag. In a shirt range, it might be XXL and up.
If a product has two attributes, only the one that changes cost matters. A candle in three scents and two sizes has six variations but only two costs, one per size.
variations that come out of the same stock
Some stores sell one bulk item in different amounts: tea by the gram, fabric by the metre, a jar sold as 1 g, 3.5 g and 7 g. Here the right cost for each variation is arithmetic, not a guess:
variation cost = amount in the variation × cost per unit of the bulk item, plus that variation’s own packaging
If the bulk item costs $2.00 a gram and every pouch costs $0.40, then the 3.5 g variation costs 3.5 × $2.00 + $0.40 = $7.40. The 7 g one costs 7 × $2.00 + $0.40 = $14.40, not double the 3.5 g figure, because the pouch is a flat cost.
Write that down once as a formula. When the supplier changes the bulk price, you recompute every size from the same line instead of editing each by feel.
where to type the cost
WooCommerce core. Recent WooCommerce versions (10.3 and later) include a cost of goods field. On a variable product, the parent’s value is a default, and each variation can carry its own. If your product edit screen shows no cost field in the pricing area, your version doesn’t have it.
A plugin. If you’d rather not depend on core’s version, or you want defaults by category and margin columns on the product list, a cost-of-goods plugin does the same job with more structure. Ours is one option (more on it below), and there are others.
Whichever you use, use one. Two cost fields on the same product, one in core and one in a plugin, is how a store ends up with two margins that disagree. Pick the one your reports read, and leave the other empty.
the traps
a parent cost that was right once
The parent cost often comes from when the product had one variation. Then sizes get added, nobody revisits the cost, and every new size inherits a number that was never about it. Whenever you add a variation, ask whether it costs the same as the default. If it doesn’t, give it its own cost at the moment you create it.
a blank cost that reads as zero
A variation with no cost and no fallback can show up in a report as a 100% margin. Treat any 100% margin as a missing cost, not a great product.
changing the cost changes the past
Many cost-of-goods setups, ours included, multiply past sales by the cost the product has today. Check which kind yours is. Raise a cost in March and January’s margins drop too. That’s fine for deciding what to stock next. It isn’t fine if you need last quarter to stay last quarter. We covered this, and the other costs a product-level margin leaves out, in how to work out profit per order in WooCommerce.
”landed” vs “purchase” cost
Decide once whether a variation’s cost is just what you paid the supplier, or that plus inbound freight and duties. Either is fine. Mixing them across products isn’t, because then the margins can’t be compared.
a fifteen-minute check you can run today
- Open your three best-selling variable products.
- For each one, list the variations and the cost each one actually uses.
- Mark every variation whose cost is inherited but shouldn’t be.
- Fix those, then look at the margins again.
If the order of your “most profitable” products changes, the old ranking was built on inherited costs.
where our plugin fits (and where it stops)
We built BigBad Store Toolkit because we had exactly this problem. It’s free on WordPress.org.
- A cost field on every product and on every variation, in the variation’s own pricing area.
- A fallback chain: the variation’s own cost, then the parent product’s, then a default set on the product category, then a global default. A missing cost is something you decided, not a silent zero.
- Bulk Apply: set, raise or lower costs by percentage, or clear them, across all products, chosen categories or tags, or products with a given attribute value. It can cascade to every variation of the matched products, and a Preview button gives you the match count before anything is saved.
- Overhead and payment fees you can subtract from the margin figures, so the margin isn’t just price minus cost of goods.
What it doesn’t do, said plainly:
- Bulk Apply by attribute matches products, not single variations. Choosing “size: 1 kg” finds every product that has a 1 kg option. With the cascade on, it then sets the cost on all of those products’ variations, not just the 1 kg ones. For a cost that only one size has, set that variation directly.
- It uses each product’s current cost for past periods, as described above.
- It keeps its own cost field. It doesn’t read or write WooCommerce core’s cost of goods field, so pick one and stay with it.
If you need cost frozen onto each order line when the sale happens, our sister product TeamBooks does that for WooCommerce stores.
Either way, start with the question that does most of the work: which of my attributes change what an item costs me? Give those variations their own number, and let everything else inherit.