How to Filter Variations by Attributes When Editing Products
Sometimes, when you have a lot of variations on your WooCommerce stores, navigating through them can be quite overwhelming.
For example, if you have 3 variation attributes with 10 values each, then you might have 3 * 3 * 10 = 90 variations! So, some of my clients do complain that the default WooCommerce variation interface is not very convenient in this specific case.
Can we do something about it?
In this tutorial, I will show you one of the ways to deal with it – and we will create a variation filter by attributes (an AJAX one).

Using woocommerce_variable_product_before_variations Action Hook to Display the Filter
First things first, we need to display the filter, right?
Luckily, WooCommerce allows us to do that without any extra hustle with the help of the action hook woocommerce_variable_product_before_variations, which will be fired right before the variations list.
Basically, here is what we need to do step by step:
- Use the
woocommerce_variable_product_before_variationshook to print the filters, - Get the product attributes used for variations from the
WC_Productobject, and using a loop, print them as<select>tags. - Depending on the type of an attribute (taxonomy-based or an individual product attribute), either get the values using the
get_terms()function or print them as is.
Let’s go ahead and do that, then.
Below is the code snippet that you can use in your WooCommerce store. If you don’t know how to do it, please read this guide.
<?php
add_action( 'woocommerce_variable_product_before_variations', 'rudr_variation_filters' );
function rudr_variation_filters() {
// the global $post variable is available for us, but not $product
global $post;
$product = wc_get_product( $post->ID );
$attributes = $product->get_attributes();
if( ! $attributes ) {
return;
}
?>
<div class="toolbar toolbar-variations-filters">
<strong>Filter variations by:</strong>
<?php
foreach( $attributes as $attribute ) {
// this attribute is not used for variations
if( ! $attribute[ 'variation' ] ) {
continue;
}
// just in case
if( empty( $attribute[ 'options' ] ) ) {
continue;
}
?><select data-attr_name="<?php echo $attribute[ 'name' ] ?>"><option value="">Any <?php echo wc_attribute_label( $attribute[ 'name' ] ) ?></option><?php
if( 0 === strpos( $attribute[ 'name' ], 'pa_' ) ) {
// it is a taxonomy-based attribute, let's use get terms
$attribute_terms = get_terms( array(
'taxonomy' => $attribute->name,
'hide_empty' => false,
'include' => $attribute[ 'options' ],
) );
if( ! $attribute_terms ) {
continue;
}
foreach( $attribute_terms as $attribute_term ) :
?><option value="<?php echo $attribute_term->slug ?>"><?php echo $attribute_term->name ?></option><?php
endforeach;
} else {
foreach( $attribute[ 'options' ] as $option ) :
?><option value="<?php echo $option ?>"><?php echo $option ?></option><?php
endforeach;
}
?></select><?php
}
?>
</div>
<?php
}Technically, we don’t need to wrap our <select> tags in a <form> tag. Using id attributes is also not necessary here.
And as a result, after using the code snippet above, we will have our filters displayed this way:

Of course, these filters do nothing right now, but we’re going to make them work in the next chapter of this guide.
Filter Variations Using AJAX
The code below is inspired by a WooCommerce file meta-boxes-product-variation.js, and also a little bit by class-wc-ajax.php.
Sending an AJAX filtering request in JavaScript (jQuery)
When you enqueue your custom JavaScript file with the code below, I think it is a good idea to add wc-admin-variation-meta-boxes as a dependency. Otherwise, there is a big chance that the woocommerce_admin_meta_boxes_variations variable will not be defined in your code.
// when we change an attribute value in its <select>
$( '.toolbar-variations-filters select' ).change( function() {
const container = $( '#woocommerce-product-data' ),
filterContainer = $(this).parent(),
wrapper = $( '#variable_product_options' ).find( '.woocommerce_variations' ),
attributes = {}
// let's loop through all the filters and prepare an object with attributes
filterContainer.find( 'select' ).each( function() {
attributes[ $(this).data( 'attr_name' ) ] = $(this).val();
});
// create a preloader
container.block( {
message: null,
overlayCSS: { background: '#fff', opacity: 0.6 },
} );
// send an AJAX request
$.ajax( {
url: woocommerce_admin_meta_boxes_variations.ajax_url,
data: {
action: 'rudr_load_variations',
security: woocommerce_admin_meta_boxes_variations.load_variations_nonce,
product_id: woocommerce_admin_meta_boxes_variations.post_id,
attributes: attributes,
},
type: 'POST',
success: function ( response ) {
wrapper.empty().append( response ).attr( 'data-page', 1 );
$( '#woocommerce-product-data' ).trigger( 'woocommerce_variations_loaded' );
container.unblock();
}
} );
} );What I also recommend doing here is to add a fallback to the default WooCommerce woocommerce_load_variations AJAX action, when all attributes are set to “Any” value. It would be a good idea to have pagination working.
But you can also split filter results into pages and code the navigation logic.
Process the request in PHP
When processing our AJAX filter request in PHP, I used some pieces of code that I got from the load_variations() method, which you can find in the class-wc-ajax.php file.
However, I decided to use the get_posts() function instead of the wc_get_products() one.
add_action( 'wp_ajax_rudr_load_variations', 'rudr_load_variations' );
function rudr_load_variations() {
check_ajax_referer( 'load-variations', 'security' );
if( ! current_user_can( 'edit_products' ) || empty( $_POST[ 'product_id' ] ) ) {
wp_die( -1 );
}
$product_id = absint( $_POST[ 'product_id' ] );
$product_object = wc_get_product( $product_id );
$args = array(
'post_type' => 'product_variation',
'post_parent' => $product_id,
'posts_per_page' => -1,
'meta_query' => array(),
);
foreach( $_POST[ 'attributes' ] as $attr_name => $attr_val ) {
if( empty( $attr_val ) ) {
continue;
}
$args[ 'meta_query' ][] = array(
'key' => 'attribute_' . $attr_name,
'value' => $attr_val,
);
}
$variations = get_posts( $args );
if( $variations ) {
wc_render_invalid_variation_notice( $product_object );
foreach ( $variations as $variation ) {
$variation_object = wc_get_product_object( 'variation', $variation->ID );
$variation_id = $variation_object->get_id();
include WC()->plugin_path() . '/includes/admin/meta-boxes/views/html-variation-admin.php';
}
}
wp_die();
}As you might notice from the code above, we can filter product variations (not products) using the meta_query argument (not tax_query).
Here you go, our brand new working filter by attributes:

What do you think about this idea, guys? Let me know in the comments below.
Misha Rudrastyh
Hey guys and welcome to my website. For more than 15 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
This looks incredibly useful! Any plans to wrap this up into a plugin so we don’t have to add code to our theme files?
Hi Steve,
Thank you!
It is already here.