Bulk convert WooCommerce simple products to variable (and back)
To bulk convert WooCommerce simple products to variable products, you change one field: the product type. In the bulk editor that takes a minute, even for thousands of products. Whether the converted products can still be sold depends on everything around that field: the type is only a label, and the prices, stock, attributes and variations a variable product needs do not come with it. Below is what happens to each field in both directions, the order of steps that keeps your prices, how to merge several simple products into one variable product, and why a type change sometimes does not stick.
What a product type change does, and what it leaves behind
WooCommerce stores the product type as a term of a hidden taxonomy, product_type, with the terms simple, grouped, external and variable. The Type column of the products table and the product type field of the Bulk Edit tab set that term and nothing else. Prices, stock, SKU and attributes stay exactly as they were stored, and what that means depends on the direction:
| Simple to variable | Variable to simple | |
|---|---|---|
| Regular and sale price | Stay stored on the product, but WooCommerce sells a variable product by the prices of its variations; the price cells of the row become read-only | Empty: WooCommerce keeps the prices of a variable product on its variations, not on the product |
| Variations | None, so the product cannot be added to the cart until it gets some | Not deleted: they stay in the database, hidden, with their prices, stock and SKUs |
| Stock | Stock management stays on the product; variations without their own stock use its quantity | The stock settings stored on the parent become the product's stock |
| SKU | Stays on the product | Stays on the product; the hidden variations keep theirs |
The second row is the main difference from the WooCommerce product editor. When you change a product from Variable to another type there, WooCommerce deletes the variations as it saves. The bulk editor changes only the type term, so the variations survive. That is useful when you switch back, because they reappear with everything they had. It is a trap when you meant to get rid of them, which is why the order of steps below matters.
Bulk convert simple products to variable without losing prices
- Select the products. In the Filters tab choose Product type: Simple, add a category, tag or any other criterion that isolates the products, and press Filter. You can also tick products in the table; ticked products take priority over the filter. Keep the variations switch above the table off: while it is on, bulk edits skip parent products.
- Change the type. Open the Bulk Edit tab, tick the checkbox next to Set: product type, choose Variable and press Do Bulk Edit. For a few products, change the Type cell directly in the table; the row is redrawn with the cells of a variable product.
- Add the attributes. Variations are built from global attributes (Products, Attributes) that are marked Used for variations on the product. How to add them in bulk with the right flags is in bulk adding attributes to products.
- Create the variations right away with Variations Advanced Bulk Operations, tab Creating: select the attributes and terms, generate the combinations and press Start BULK Adding!. The full procedure is in bulk creating variations.
Step 4 is where the price is kept. When the generator creates a variation, it copies the regular and sale price stored on the parent product into it. A product that was simple a minute ago still has its old prices stored, so every new variation starts with the price the simple product had. This works once per product: as soon as a product has variations, WooCommerce syncs it and clears the parent's own price fields, and variations created by a later run get no price. Only the prices are copied, not a sale schedule: a sale planned on the simple product for next month starts at once on its variations, so reschedule it as in scheduling sale prices in bulk. Create all the combinations a product needs in one run, then change the prices that differ, for example a higher price for XXL, as shown in different prices for different variations. Why the parent row has no editable price from now on is explained in variable product price field disabled.
Stock follows the same logic. New variations are created without their own stock management. If the simple product managed its stock, that setting stays on the parent, and WooCommerce lets every variation without its own stock sell from the parent's quantity: 50 units of the old product become 50 units shared by all sizes. To count each variation separately, turn the variations switch on and set Manage stock to Yes in one Bulk Edit run, then the quantities in a second run; the reason for two runs is in stock status not changing.
Convert variable products to simple in bulk
The order matters here, because the tool that deletes variations only works on products that are still variable:
- Filter the products by Product type: Variable plus the category or tag that selects them. If you want to keep a price, write it down now: turn the variations switch on to see the variation prices, then turn it off again.
- Delete the variations in Variations Advanced Bulk Operations, tab Deleting: choose Delete all products variations and press Start deleting. First read the line that says what the operation will be applied to: with nothing filtered or ticked it says all the products on the site. Deleted variations cannot be restored and are not recorded in History. The FAQ on removing all variations shows the screen.
- Change the type: Bulk Edit tab, Set: product type, Simple, Do Bulk Edit.
- Set the prices. The Regular price and Sale price cells are editable again. Filter by Product type: Simple plus the same category and set the price for all of them in the Bulk Edit tab, or cell by cell where prices differ.
If you change the type first, the Deleting tab skips the products because they are no longer variable, and the variations stay behind. Customers never see them, but their SKUs still count: WooCommerce refuses a SKU that any other product or variation already has, so the simple product cannot take the SKU of one of its former variations. To clean up, switch the products back to Variable (the variations reappear), delete the variations, and switch to Simple again.
Keep the price of the variation automatically
When each variable product has a single variation, or all its variations share one price, a one-off snippet does the conversion the way the WooCommerce editor does it and keeps the price. It loads each product as a simple product, copies the regular price, sale price and stock of its first variation, and saves it; during that save WooCommerce changes the type and deletes the variations itself. Back up the database first, put your product IDs into the list, open /wp-admin/?woo_variable_to_simple=1 once as an administrator, then remove the code.
/*
* One-off: convert variable products to simple products and keep the
* price and stock of their first variation. WooCommerce deletes the variations.
* Child theme functions.php or a code snippets plugin; remove after use.
*/
add_action( 'admin_init', function () {
if ( empty( $_GET['woo_variable_to_simple'] ) || ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
$product_ids = array( 101, 102, 103 ); // IDs of the variable products
foreach ( $product_ids as $product_id ) {
$variable = wc_get_product( $product_id );
if ( ! $variable || ! $variable->is_type( 'variable' ) ) {
continue;
}
$children = $variable->get_children();
$variation = $children ? wc_get_product( $children[0] ) : null;
$simple = new WC_Product_Simple( $product_id );
if ( $variation ) {
$simple->set_regular_price( $variation->get_regular_price( 'edit' ) );
$simple->set_sale_price( $variation->get_sale_price( 'edit' ) );
if ( $variation->get_manage_stock( 'edit' ) ) {
$simple->set_manage_stock( true );
$simple->set_stock_quantity( $variation->get_stock_quantity( 'edit' ) );
} elseif ( ! $simple->get_manage_stock( 'edit' ) ) {
$simple->set_stock_status( $variation->get_stock_status( 'edit' ) );
}
}
$simple->save(); // WooCommerce switches the type and deletes the variations here
}
wp_die( 'Done. Remove the code now.' );
} );
The product keeps its own SKU. Check two or three converted products on the shop before you remove the code.
Merge several simple products into one variable product
Often the simple products already are the variations: T-shirt S, T-shirt M, T-shirt L. Converting them does not help, because each would become a variable product of its own. What you need is a new variable product whose variations take over the price, SKU and stock of the old products. The bulk editor cannot do this by itself: nothing in a product says which attribute value it stands for, the size is just text in the title.
For a few groups, do it in the editor: create the variable product, add the attribute, generate the variations with Variations Advanced Bulk Operations, then turn the variations switch on and type prices, SKUs and stock into the variation rows. Remove the SKU from the old simple product before you give it to a variation, since WooCommerce does not allow the same SKU twice.
For many products, the snippet below builds one variable product from a list of simple product IDs and their attribute values. It copies the description, categories and image of the first product to the new parent, creates one variation per simple product with its prices, SKU, stock, weight and image, and sets the old products to draft. Run it once per group: edit the three settings, open /wp-admin/?woo_merge_simple=1 as an administrator, and you land in the editor of the new product, which is a draft until you publish it.
/*
* One-off: merge simple products into one new variable product.
* Child theme functions.php or a code snippets plugin; remove after use.
*/
add_action( 'admin_init', function () {
if ( empty( $_GET['woo_merge_simple'] ) || ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
$title = 'Classic T-shirt'; // name of the new product
$attribute = 'Size'; // what the variations differ by
$map = array( 101 => 'S', 102 => 'M', 103 => 'L' ); // simple product ID => value
$first = wc_get_product( array_key_first( $map ) );
if ( ! $first ) {
wp_die( 'Check the product IDs.' );
}
$parent = new WC_Product_Variable();
$parent->set_name( $title );
$parent->set_status( 'draft' );
$parent->set_description( $first->get_description( 'edit' ) );
$parent->set_short_description( $first->get_short_description( 'edit' ) );
$parent->set_category_ids( $first->get_category_ids( 'edit' ) );
$parent->set_image_id( $first->get_image_id( 'edit' ) );
$attr = new WC_Product_Attribute();
$attr->set_name( $attribute );
$attr->set_options( array_values( $map ) );
$attr->set_visible( true );
$attr->set_variation( true );
$parent->set_attributes( array( $attr ) );
$parent_id = $parent->save();
foreach ( $map as $simple_id => $value ) {
$simple = wc_get_product( $simple_id );
if ( ! $simple || ! $simple->is_type( 'simple' ) ) {
continue;
}
$sku = $simple->get_sku( 'edit' );
// free the SKU and take the old product offline
$simple->set_sku( '' );
$simple->set_status( 'draft' );
$simple->save();
$variation = new WC_Product_Variation();
$variation->set_parent_id( $parent_id );
$variation->set_attributes( array( sanitize_title( $attribute ) => $value ) );
$variation->set_regular_price( $simple->get_regular_price( 'edit' ) );
$variation->set_sale_price( $simple->get_sale_price( 'edit' ) );
$variation->set_sku( $sku );
$variation->set_manage_stock( $simple->get_manage_stock( 'edit' ) );
$variation->set_stock_quantity( $simple->get_stock_quantity( 'edit' ) );
$variation->set_stock_status( $simple->get_stock_status( 'edit' ) );
$variation->set_weight( $simple->get_weight( 'edit' ) );
$variation->set_image_id( $simple->get_image_id( 'edit' ) );
$variation->save();
}
WC_Product_Variable::sync( $parent_id );
wp_safe_redirect( admin_url( 'post.php?post=' . $parent_id . '&action=edit' ) );
exit;
} );
The attribute is created on the product itself, not under Products, Attributes. That is enough for the variations to work. If you want to filter the shop by it or reuse it on other products, turn it into a global attribute afterwards, as described in converting custom attributes to global ones. The old product URLs now lead nowhere; redirect them to the new product with a redirect plugin or a server rule.
The product type change is reported but the product stays simple
After a change in the Type cell the editor shows Saved and redraws the row with data read back from the server. The message only means the request finished; what is really stored shows in the redrawn row and in the WooCommerce product editor, in the Product data dropdown. Compare the two with the shop:
- Variable in the product editor, simple on the shop. The shop page comes from a page cache or a CDN. Purge it; changes not showing on the frontend lists the cache layers to check.
- Changed in the table, back to simple after a reload. Something serves old data to the admin. The usual causes are a hosting-level server cache that also caches
/wp-admin/oradmin-ajax.php, and caching plugins that keep database results in a persistent object cache. Exclude the admin andadmin-ajax.phpfrom page caching and flush the object cache. Excluding the plugin's JavaScript and CSS from optimisation does not help, because the stale data sits on the server. - Some products of a Bulk Edit run did not change. They were outside the filter or the ticked set, or the variations switch was on and the run skipped all parent products.
To undo a type change made in a cell, revert it in History. A Bulk Edit run is different: in version 2.2.4, rolling back a run that changed the product type leaves the new type in place. Undo it by selecting the same products again and running the Bulk Edit tab with the old type.
Checklist
- Back up the database: deleted variations and code runs cannot be rolled back.
- Simple to variable: type, attributes, all variations in one run, then prices and stock per variation.
- Variable to simple: variations first, then the type, then the prices; the variations switch stays off for type changes.
- Check one converted product in the WooCommerce editor and on the shop before the next category.
If a conversion behaves differently on your shop, describe the steps you took on the BEAR support forum.