A variable product is one product sold in several forms: sizes, colours, materials. WooCommerce builds it from attributes you define and variations you generate, and the order of those two steps is what most of the difficulty comes from.
Attributes first, then variations
An attribute is the characteristic: Size, Colour.
A variation is one specific combination: Medium in Blue.
You cannot create variations until the attributes exist, and changing attributes afterwards invalidates variations that used them. Decide the attributes properly before generating anything.
Global or per-product attributes
This is the decision worth getting right, because moving from one to the other later means editing every product.
Global attributes are defined once under Products → Attributes and reused. Size defined once, used by every garment.
Custom product attributes exist on one product only.
Use global for anything appearing on more than one product. Only global attributes can be used in layered navigation. The filters that let customers narrow a category by size or colour, and rebuilding a catalogue to add filtering later is a genuinely tedious job.
Keep custom attributes for the genuinely one-off: a single product with an unusual option no other product shares.
Setting it up
In the product's data panel, set the type to Variable product.
Under Attributes, add each one. Select its values, and tick Used for variations: an attribute without that box is displayed as information and cannot form a variation. Missing it is the most common reason the variations tab appears empty.
Then under Variations, generate from all attributes, or add them one at a time.
Do not generate every combination
Generating from all attributes creates the full grid: four sizes and five colours is twenty variations, whether or not all twenty exist.
Combinations you do not stock still appear in the dropdowns, and a customer who selects one gets an unavailable message after doing the work of choosing. That is a bad enough experience to lose the sale.
Either add variations individually for what you actually sell, or generate the grid and delete what does not exist. Two attributes with several values each escalates quickly, and every variation is a row in the database that loads with the product page.
Each variation carries its own data
Once a variation exists, it can override almost everything about the product.
Price. Leave it empty and the variation cannot be purchased. This is the number one cause of "the add to cart button does nothing" on a variable product.
SKU. Give each variation its own if you track stock outside the shop.
Stock. Enable management per variation rather than at the product level, otherwise the shop knows you have twelve without knowing which sizes, and a customer can buy a size you have none of. Inventory and stock management goes into this properly.
Weight and dimensions. Set them if variations differ physically, because shipping is calculated from them.
Image. One per variation, shown when the customer selects it.
The defaults field
Under the variations list you can set default selections. The options pre-chosen when the page loads.
Set them when one combination is the obvious common choice, because it removes a step. Leave them empty when the choice is genuinely open, since a pre-selected size can be missed and produce the wrong order.
Why the price shows a range
A variable product with different prices displays "£20 – £35" until a customer chooses. That is correct behaviour and it confuses shoppers on category pages.
If the range is wide because of one expensive variation, consider whether it belongs as a separate product. A range from £20 to £200 tells the customer almost nothing.
Performance, which is a real limit
Variations are stored as separate posts with their own metadata. A product with fifty variations is fifty extra records loaded when that page renders.
A handful of products like that is fine. A catalogue built that way is slow, and the slowness appears on the product page. The page where you are asking for the sale.
Two attributes is comfortable. Three is where it starts to hurt. Beyond that, ask whether these are really variations of one product or several products. For the wider picture, see WooCommerce speed optimization.
When variations do not appear on the front end
Four causes, in the order they occur.
The attribute is not marked "used for variations".
A variation has no price. It is skipped entirely.
It is out of stock with backorders disabled.
The theme overrides the variation template and its version is out of date. Switch to a default theme briefly to confirm. The WooCommerce troubleshooting guide deals with isolating that.
Check it as a customer
Open the product page and buy something. Select each combination, watch the price and image update, and complete an order for one variation.
Then confirm the order shows which variation was bought. A shop that records "T-shirt" without the size produces a fulfilment problem that only appears once real orders are arriving.
Count the variations before creating them
Options multiply, and the total is easy to underestimate until the product page will not load.
wp db query "SELECT p.post_parent, COUNT(*) c FROM wp_posts p WHERE p.post_type='product_variation' GROUP BY p.post_parent ORDER BY c DESC LIMIT 10;" wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_type='product_variation';"
Three attributes with five values each is a hundred and twenty five records for one product, each with its own metadata. A catalogue built that way grows far faster than the product count suggests.
Generate only the combinations you actually sell. A product with more variations than the shop has products is a sign the options were generated wholesale rather than chosen, and it is the usual reason a single product page becomes the slowest on the site.
Fill in the fields the customer never sees
Each variation is effectively its own product, and the fields left empty are the ones that fail later.
wp db query "SELECT COUNT(*) FROM wp_posts p LEFT JOIN wp_postmeta m ON m.post_id=p.ID AND m.meta_key='_price' WHERE p.post_type='product_variation' AND (m.meta_value IS NULL OR m.meta_value='');"
A variation with no price cannot be bought, and the product page shows it as an option that produces no button when selected.
Weight and stock behave the same way. A variation with no weight breaks shipping for any basket containing it, and one with stock management enabled and no quantity is permanently out of stock. Neither reports anything; the customer simply cannot complete the order. Setting up shipping zones covers the weight side.