How to Use WooCommerce Attributes Without Creating 300 Variations

Hey, I ran into the same problem (huge variation explosions) and ended up hacking together a small single-file plugin that does exactly what you’re describing.

What it does

  • You keep using global attributes (Size, Color, Material, etc.).
  • Any attribute that is “Used for variations” works as normal and controls price/stock.
  • Any global attribute that is notmarked “Used for variations” gets output as an extra dropdown on the product page, and the chosen value is:
    • added to the cart item data
    • shown on Cart / Checkout
    • saved on the order line item in the admin

That means you can do:

  • Product 1 → variations only on Size, with Color + Material as “just choices”
  • Product 2 → variations on Size + Material, with Color as “just a choice”

…and you don’t have to generate 20×5×3 = 300 variations anymore.

How to use it

  1. Create a file in wp-content/plugins/, e.g. choice-attributes-for-woocommerce.php
  2. Paste the code below into it.
  3. Activate it in Plugins → Installed Plugins.
  4. On a product:
    • Add your global attributes under Attributes.
    • For attributes that should control price/stock, check “Used for variations”.
    • For attributes that should just be dropdown choices, leave “Used for variations” unchecked (but keep them visible on the product page).

You can also just add it as a PHP Snippet if you have codesnippet plugin installed.

On the front end you’ll see:

  • normal variation selectors for the variation attributes, plus
  • extra selects for the “choice-only” attributes, and those choices will be saved to the order.

Here is the code:

<?php
/**
* Plugin Name: LSB Choice Attributes for WooCommerce
* Description: Treat non-variation global attributes as dropdown choices saved to cart and order, without generating extra variations.
* Version: 1.0.0
* Author: LocalSiteBuilder
* Text Domain: lsb-choice-attributes
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Check that WooCommerce is active.
*/
function lsbca_is_woocommerce_active() {
return class_exists( 'WooCommerce' );
}

/**
* Render choice-only attribute dropdowns on the single product page.
*
* Rule:
* - Any GLOBAL attribute (taxonomy) added to the product
* - That is NOT marked "Used for variations"
* becomes a "choice" select box.
*/
function lsbca_render_choice_attributes() {
if ( ! lsbca_is_woocommerce_active() ) {
return;
}

global $product;

if ( ! $product instanceof WC_Product ) {
return;
}

// Get all attributes assigned to this product.
$attributes = $product->get_attributes();

if ( empty( $attributes ) ) {
return;
}

$choice_attributes = array();

foreach ( $attributes as $attribute ) {
// We only care about global attributes (taxonomies like pa_color).
if ( ! $attribute->is_taxonomy() ) {
continue;
}

// Skip attributes that are used for variations.
if ( $attribute->get_variation() ) {
continue;
}

$taxonomy = $attribute->get_name(); // e.g. 'pa_color'

// Make sure taxonomy still exists.
if ( ! taxonomy_exists( $taxonomy ) ) {
continue;
}

$choice_attributes[ $taxonomy ] = $attribute;
}

if ( empty( $choice_attributes ) ) {
return;
}

echo '<div class="lsbca-choice-attributes">';

foreach ( $choice_attributes as $taxonomy => $attribute ) {
$label = wc_attribute_label( $taxonomy );
$options = $attribute->get_options();

if ( empty( $options ) ) {
continue;
}

// Build a select field name like: lsb_choice_attributes[pa_color]
$field_name = 'lsb_choice_attributes[' . esc_attr( $taxonomy ) . ']';

echo '<div class="lsbca-choice-attribute">';
echo '<label for="' . esc_attr( $taxonomy ) . '"><strong>' . esc_html( $label ) . '</strong></label><br />';
echo '<select id="' . esc_attr( $taxonomy ) . '" name="' . esc_attr( $field_name ) . '">';
echo '<option value="">' . sprintf( esc_html__( 'Choose %s', 'lsb-choice-attributes' ), esc_html( strtolower( $label ) ) ) . '</option>';

// $options are term IDs for taxonomy attributes.
foreach ( $options as $term_id ) {
$term = get_term( $term_id, $taxonomy );
if ( ! $term || is_wp_error( $term ) ) {
continue;
}

$term_value = (string) $term->term_id;
$term_name = $term->name;

printf(
'<option value="%s">%s</option>',
esc_attr( $term_value ),
esc_html( $term_name )
);
}

echo '</select>';
echo '</div>';
}

echo '</div>';
}
add_action( 'woocommerce_before_add_to_cart_button', 'lsbca_render_choice_attributes', 25 );

/**
* Store the chosen "choice-only" attributes in cart item data.
*/
function lsbca_add_choice_attributes_to_cart_item( $cart_item_data, $product_id, $variation_id ) {
if ( ! lsbca_is_woocommerce_active() ) {
return $cart_item_data;
}

if ( empty( $_POST['lsb_choice_attributes'] ) || ! is_array( $_POST['lsb_choice_attributes'] ) ) {
return $cart_item_data;
}

$choice_attributes = array();

foreach ( $_POST['lsb_choice_attributes'] as $taxonomy => $term_id ) {
$taxonomy = sanitize_text_field( wp_unslash( $taxonomy ) );
$term_id = absint( $term_id );

if ( ! $term_id || ! taxonomy_exists( $taxonomy ) ) {
continue;
}

$term = get_term( $term_id, $taxonomy );
if ( ! $term || is_wp_error( $term ) ) {
continue;
}

// Store the term ID and taxonomy so we can look it up later.
$choice_attributes[ $taxonomy ] = $term_id;
}

if ( ! empty( $choice_attributes ) ) {
// Merge so we don't overwrite any existing custom cart item data.
$cart_item_data['lsb_choice_attributes'] = $choice_attributes;
}

return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'lsbca_add_choice_attributes_to_cart_item', 10, 3 );

/**
* Display the choice-only attributes in cart/checkout line items.
*/
function lsbca_display_choice_attributes_in_cart( $item_data, $cart_item ) {
if ( empty( $cart_item['lsb_choice_attributes'] ) || ! is_array( $cart_item['lsb_choice_attributes'] ) ) {
return $item_data;
}

foreach ( $cart_item['lsb_choice_attributes'] as $taxonomy => $term_id ) {
$term = get_term( $term_id, $taxonomy );
$label = wc_attribute_label( $taxonomy );
$value = $term && ! is_wp_error( $term ) ? $term->name : '';

if ( ! $label || ! $value ) {
continue;
}

$item_data[] = array(
'name' => $label,
'value' => $value,
);
}

return $item_data;
}
add_filter( 'woocommerce_get_item_data', 'lsbca_display_choice_attributes_in_cart', 10, 2 );

/**
* Save choice-only attributes as order item meta.
*/
function lsbca_add_choice_attributes_to_order_items( $item, $cart_item_key, $values, $order ) {
if ( empty( $values['lsb_choice_attributes'] ) || ! is_array( $values['lsb_choice_attributes'] ) ) {
return;
}

foreach ( $values['lsb_choice_attributes'] as $taxonomy => $term_id ) {
$term = get_term( $term_id, $taxonomy );
$label = wc_attribute_label( $taxonomy );
$value = $term && ! is_wp_error( $term ) ? $term->name : '';

if ( ! $label || ! $value ) {
continue;
}

// This will show up on the order line item like a normal attribute/meta pair.
$item->add_meta_data( $label, $value, true );
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'lsbca_add_choice_attributes_to_order_items', 10, 4 );
Shopping Cart
🎁 Join now to earn Points
Scroll to Top