WooCommerce Variable Product CSVs: Parent Rows, Variations, and Attribute Columns
A WooCommerce variable product is not a single CSV row. It is a parent row with Type set to variable that defines the attributes, plus one child row per variation with Type set to variation that links back through the Parent column. When variations import as separate simple products, show blank options, or get skipped entirely, the cause is almost always a broken parent-to-child link or a variation value that does not exactly match the parent. This guide covers the exact columns the native WooCommerce product CSV importer expects for variable products only, and the structural mistakes that quietly break the import.
If you are chasing generic importer errors (missing columns, encoding, wrong delimiters), that is a separate problem. This post is only about the parent-plus-variation structure that trips up variable products specifically.
How does a variable product map to CSV rows?
Each variable product spans at least two rows. The first is the parent: one row with Type set to variable. It holds the product name, description, categories, images, and the full list of possible attribute values. It does not hold a price. Below it sit the variation rows: one row per purchasable combination, each with Type set to variation, a unique SKU, its own price, and a single value for each variation attribute.
The Type column in the native importer accepts simple, variable, grouped, external, variation, virtual, and downloadable. For our purposes only two matter: variable on the parent, variation on every child. Get these two values right and half the common failures disappear.
The minimum columns a variable-product CSV needs:
- Type - variable on the parent row, variation on each child row
- SKU - unique on every row, including each variation
- Name - filled on the parent; can be left blank on variation rows
- Parent - empty on the parent; set to the parent's SKU or id: on each variation
- Regular price - empty on the parent; set on each variation row
- Attribute 1 name / value(s) / visible / global / default - the attribute set
Why must the parent import before its variations?
A variation has no independent existence. It attaches to a parent, so the parent must exist first. The native WooCommerce importer resolves parent references within a single file, but it reads top to bottom, so the parent row should always sit above its variation rows. If you split the data across two files (a common pattern with large catalogs), you must import the parent products first and the variations second, or every variation row fails to find its parent.
The link itself lives in the Parent column. WooCommerce documents it as: set parent ID, used for variations, can be a numeric ID like id:100 or a SKU. So a variation row points home in one of two ways: the parent's SKU (for example TSHIRT) or the database ID prefixed with id: (for example id:100). SKU is the safer choice when you are building the file by hand, because you control it and it does not change between environments.
The single most common variable-product failure: a variation row with the Parent column left empty. WooCommerce cannot orphan a variation, so it either skips the row or imports it as a standalone simple product cluttering your catalog. Always fill Parent on every variation row.
The attribute columns explained
Attributes are described by a repeating group of columns, numbered per attribute. For the first attribute the exact headers are Attribute 1 name, Attribute 1 value(s), Attribute 1 visible, Attribute 1 global, and Attribute 1 default. A second attribute (say Color alongside Size) uses Attribute 2 name, Attribute 2 value(s), and so on. Here is what each column does and how it differs between the parent and the variation rows.
What each attribute column means:
- Attribute 1 name - the attribute label, for example Size. Identical on the parent and every variation that uses it.
- Attribute 1 value(s) - on the parent, the full list of possible values; on a variation, the single value for that one variation.
- Attribute 1 visible - 1 or 0. Controls whether the attribute shows in the product's Additional Information tab. Set on the parent.
- Attribute 1 global - 1 or 0. 1 means a global (taxonomy) attribute; 0 means a local custom attribute defined only on this product.
- Attribute 1 default - the pre-selected value shown to shoppers before they pick. Set on the parent only.
The parent-versus-variation split in the value(s) column is where files break. On the parent row, Attribute 1 value(s) lists every value the variations will use. The WooCommerce schema separates multiple values with commas inside a quoted field, for example "Small, Medium, Large". On each variation row, Attribute 1 value(s) holds exactly one value that must be an exact match to one of the parent's values.
Exact match matters: a variation value of "small" or "Small " (trailing space) will not match a parent value of "Small". The variation imports but its option renders as blank or Any, so it can never be selected. Case, spelling, and whitespace all have to line up with the parent's list.
Global vs local attributes and the global flag
Attribute 1 global tells WooCommerce which kind of attribute you mean. Set it to 1 for a global attribute, the kind managed under Products then Attributes and stored as a taxonomy with a pa_ slug prefix (a Color attribute becomes pa_color internally). Global attributes are reusable across products and are referenced by their display name in the CSV; the importer resolves the slug for you. Set it to 0 for a local, custom attribute that exists only on that single product and is not shared.
Mixing the two is a subtle trap. If your store already defines Size as a global attribute but your CSV sets Attribute 1 global to 0, WooCommerce creates a second, product-local Size that looks identical in the admin but is a different attribute under the hood. Variations then reference values that the shopper-facing dropdown does not offer. Decide up front whether each attribute is global (1) or local (0) and keep it consistent across the parent and all variations.
Where is the 'used for variations' flag?
In the WooCommerce product editor, each attribute has a Used for variations checkbox. New importers look for a matching CSV column and cannot find one, because the native importer has no separate used-for-variations header. Instead, it infers the flag: when the parent Type is variable and variation rows reference an attribute, that attribute is automatically marked used for variations. Your job is simply to make the structure correct: variable parent, a listed attribute, and variation rows whose single values come from that attribute's parent list. Get that right and the checkbox takes care of itself.
SKUs, prices, and images per variation
Every row needs a unique SKU, and variations are where duplicates sneak in. A blank SKU is auto-generated, but two variation rows sharing a SKU (or sharing the parent's SKU) throws a duplicate error and drops rows. Give the parent a base SKU like TSHIRT and each variation a distinct one like TSHIRT-S, TSHIRT-M, TSHIRT-L.
Pricing belongs on the variation rows, not the parent. Leave the parent's Regular price and Sale price empty; a variable product has no single price of its own. Put the actual price in each variation row's Regular price (and optional Sale price). A price accidentally left on the parent is a frequent reason a variable product behaves oddly at checkout or in reports.
Images work per row too. The parent's Images column sets the main product gallery, where the first URL is the featured image. Each variation row can carry its own single image in its Images column, which is what swaps in when a shopper selects that variation.
Before and after: a broken block vs a correct one
Here is a t-shirt in three sizes, done the way it usually arrives from a spreadsheet export. Every row is Type simple, nothing links to a parent, the price sits on what should be the parent, and the attribute values are per-row singles with no parent list:
Type,SKU,Name,Parent,Regular price,Attribute 1 name,Attribute 1 value(s),Attribute 1 visible,Attribute 1 global
simple,TSHIRT,Cotton T-Shirt,,19.99,Size,Small,1,1
simple,TSHIRT,Cotton T-Shirt,,19.99,Size,Medium,1,1
simple,TSHIRT,Cotton T-Shirt,,21.99,Size,Large,1,1That import produces three separate simple products (or a duplicate-SKU error), no variable product, and no size dropdown. Now the corrected structure: one variable parent with the full value list, an empty price, and a default, followed by three variation rows that link back via Parent and each carry a single matching value, a unique SKU, and their own price:
Type,SKU,Name,Parent,Regular price,Attribute 1 name,Attribute 1 value(s),Attribute 1 visible,Attribute 1 global,Attribute 1 default
variable,TSHIRT,Cotton T-Shirt,,,Size,"Small, Medium, Large",1,1,Small
variation,TSHIRT-S,,TSHIRT,19.99,Size,Small,1,1,
variation,TSHIRT-M,,TSHIRT,21.99,Size,Medium,1,1,
variation,TSHIRT-L,,TSHIRT,23.99,Size,Large,1,1,Note the differences: the parent row's Type is variable with an empty Regular price and a comma-joined, quoted value list; each variation's Type is variation, its Parent is the parent SKU TSHIRT, its value(s) is a single exact match, its SKU is unique, and only variations carry prices. The Attribute 1 default column is set on the parent and left empty on variations.
A pre-import checklist for variable products
Run through this before you upload:
- Parent rows have Type set to variable; every child has Type set to variation.
- Every variation row's Parent column is filled with the parent SKU or id: value.
- Parent rows have empty Regular price and Sale price; variations carry the prices.
- The parent's Attribute value(s) lists every value; each variation lists exactly one.
- Variation values match the parent list exactly (case, spelling, no trailing spaces).
- Attribute global is consistent (1 for global/taxonomy, 0 for local) across parent and children.
- Every SKU is unique; no variation reuses the parent's SKU or another variation's.
- Parent rows appear above their variations in the file, or parents are imported first.
Cleaning the file before you import
Most of the failures above are data-hygiene problems, not WooCommerce problems. Trailing spaces on a variation value, inconsistent casing between Small and small, stray null placeholders like N/A in the price column, and mismatched header names are exactly the kind of thing that passes a visual glance but breaks the parent-to-child match. Excel makes it worse by stripping leading zeros from SKUs and reformatting anything it thinks is a number or date.
This is where a dedicated cleaning pass helps. In PipeSheets you can upload the CSV, run Quick Clean to trim whitespace, standardize null values, and normalize headers, then use find and replace or a case transform to force your variation values into one consistent spelling that matches the parent list. The before/after preview shows detected column types, so you can confirm SKUs stayed text and prices stayed numeric before you export a clean CSV for WooCommerce.
PipeSheets does not map your columns to WooCommerce or validate the import for you, and it will not invent a parent row you did not write. What it does well is the tedious normalization that makes the parent-and-variation structure hold together: consistent casing, no stray whitespace, clean headers, and values that stay in the format WooCommerce expects. Fix the structure by hand, clean the data with a repeatable pipeline, and your variable products import as variable products the first time.
Related guides
- How to Fix WooCommerce CSV Import Errors (Product Importer Guide)The WooCommerce product importer rejects files for invalid file types, unmapped columns, bad encoding, and malformed booleans. Here's what each error actually means and how to fix your CSV fast.
- Supplier Price List Cleanup for Marketplace Uploads (Shopify, Amazon, eBay)A supplier price list is built for humans and accounting, not for Shopify, Amazon, or eBay. Here is the field-by-field workflow to turn one into a marketplace upload without listing your cost as your retail price.
- Normalize CSV Column Headers Before Import: Fix Mapping Errors FastWhen an import fails at the field-mapping step, the data is usually fine — the headers aren't. Here's how to normalize column headers so mapping just works.
- Normalize Vendor Catalogs from Multiple Suppliers (Without Going Insane)Every vendor sends product data differently. Here's how to wrangle them all into one consistent format for your inventory system.
Related tools & guides
Try the automated solution
PipeSheets can fix these issues automatically. Clean your first file free.
Clean Your CSV