Display Product Variations Dropdown on the Shop Page

In this tutorial I am going to show how you can create your own variation dropdowns on the shop page (or any archive product page). When I’m saying “creating your own”, I mean it exactly, so we don’t just copy and paste a couple of hooks from single product pages.

Our another goal here is making sure that AJAX add to cart buttons are working great for our custom variation dropdowns as well.

Here on the screenshot I am showing what exactly we are going to create in this guide:

custom variations select dropdown on the shop page
Selecting a variation with two attributes on the Shop page and adding it to cart with AJAX.

And also If you would like something more interesting, like on the screenshot below, with variation swatches, then consider checking my variation swatches plugin.

display color swatches for product variations on the shop page

Now let’s start the coding. I decided to use woocommerce_loop_add_to_cart_link filter hook because it allows to remove and modify “Select options” button and also to return “Add to cart” buttons for simple products without changes.

<?php
add_filter( 'woocommerce_loop_add_to_cart_link', 'rudr_select_variations_shop_page', 99, 2 );

function rudr_select_variations_shop_page( $add_to_cart, $product ) {

	// do nothing in case it is not a variable product
	if( ! $product->is_type( 'variable' ) ) {
		return $add_to_cart;
	}

	// if there is no variations available for purchase, also do nothing
	if( ! $variations = $product->get_available_variations() ) {
		return $add_to_cart;
	}

	ob_start();
	?>

	<div class="rudr_variations" data-product_variations="<?php echo wc_esc_json( wp_json_encode( $variations ) ) ?>">

		<?php 
			foreach( $product->get_attributes() as $attribute_name => $attribute ) :
				?>
					<div class="rudr-variation-select value">
						<?php
							wc_dropdown_variation_attribute_options(
								array(
									'show_option_none' => 'Select ' . strtolower( wc_attribute_label( $attribute_name ) ),
									'options' => $attribute->get_slugs(),
									'attribute' => $attribute_name,
									'product' => $product
								)
							);
						?>
					</div>
				<?php 
			endforeach;
		?>

		<a href="" data-product_id="" class="disabled button add_to_cart_button">Add to cart</a>

	</div>
	<?php
	
	return ob_get_clean();

}

And also:

Do you think, that’s all?

Of course not, we also need some JavaScript as well.

jQuery( function( $ ) {

	$( '.rudr-variation-select' ).find( 'select[data-attribute_name]' ).change( function() {

		const el = $(this);
		const variationsWrapper = el.closest( '.rudr_variations' );
		const availableVariations = JSON.parse( variationsWrapper.attr( 'data-product_variations' ) );
		const productWrapper = variationsWrapper.closest( 'li.product' );
		const button = productWrapper.find( 'a.add_to_cart_button' );

		// our matching variation will be stored in this object
		let matchingVariation = {};

		// now we can loop through all the variations and check them against the selected attributes
		for( let i = 0; i < availableVariations.length; i++ ) {
			// assign it as a matching variation in any case
			matchingVariation = availableVariations[i];

			// let's check all the attributes of this variation
			for( const [ attr_name, attr_value ] of Object.entries( availableVariations[i].attributes ) ) {
	  		// if any of attributes doesn't match
				if( attr_value !== variationsWrapper.find( '[name="' + attr_name + '"]' ).val() ) {
					matchingVariation = {};
				}
			}

			// exit the loop if we have a matching variation
			if( Object.keys( matchingVariation ).length !== 0 ) {
				break;
			}

		}

		// add a variation ID to the Add to cart button
		if( matchingVariation.variation_id ) {
			button.removeClass( 'disabled' ).addClass( 'ajax_add_to_cart' );
			button.attr( 'data-product_id', matchingVariation.variation_id );
		}
		// update product price
		if( matchingVariation.price_html ) {
			productWrapper.find( 'span.price').replaceWith( matchingVariation.price_html );
		}
		// update product image
		if( matchingVariation.image ) {
			productWrapper.find( 'img' ).attr( {
				src: matchingVariation.image.src,
				srcset: matchingVariation.image.srcset,
				width: matchingVariation.image.src_w,
				height: matchingVariation.image.src_h
			} );
		}

	} );

} );
Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 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

Follow me on X