One of the challenging parts  while running an online and a physical store, or multiple physical stores, is managing stock availability and inventory. You have to keep track of products that customers are interested in. You need to analyze buying behavior, predict consumer demand, and decide on stocking options. 

In our Product Listing Page (PLP), we generally show all the products by different orders. We also display the products that are out of stock. WooCommerce by default gives us some sorting option like sort by rating, sort by price, sort by popularity etc.

So if some products are out of stock still it would be in the beginning, which can lead to bad user experience. We should display in the end. We can do it in two different way.

  • Sort everything by _stock_status & ignore other filtering options.
  • Keep out of stock elements in the end & keep the filtering option intact
Sort every items by stock status (hide filtering option)

First we’ll remove the Default Sorting Dropdown.

// Remove “Default Sorting” Dropdown @ WooCommerce Shop & Archive Pages
remove_action( 'woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 30 );
// Remove “Default Sorting” Dropdown in StoreFront theme
add_action( 'wp', 'wpdrift_remove_default_sorting_storefront' );
function wpdrift_remove_default_sorting_storefront() {
   remove_action( 'woocommerce_after_shop_loop', 'woocommerce_catalog_ordering', 10 );
   remove_action( 'woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 10 );

Now we need to use woocommerce_get_catalog_ordering_args hook to sort products by “stock_status” in “ASC” (ascending) order. Possible values are “instock”, “onbackorder”, “outofstock” so it will automatically display them alphabetically like this: instock, onbackorder, outofstock.

add_filter( 'woocommerce_get_catalog_ordering_args', 'wpdrift_first_sort_by_stock_count', 9999 );

//Sort Products By Stock Status
function wpdrift_first_sort_by_stock_count( $args ) {
   $args['orderby'] = 'meta_value';
   $args['order'] = 'ASC';
   $args['meta_key'] = '_stock_status';
   return $args;
Keep out of stock elements in the end & keep the filtering option intact

In the previous way, we have some issues. Though we are achieving the result we wanted but we had to remove the sorting option. Now we’ll try to achieve the result without removing the sorting option. We are using woocommerce_get_catalog_ordering_args & posts_clauses hooks to achieve the result.

add_filter( 'woocommerce_get_catalog_ordering_args', 'wpdrift_catalog_ordering_args' );
function wpdrift_catalog_ordering_args( $args ) {
	add_filter( 'posts_clauses', 'wpdrift_filter_posts_clauses', 9999 );
	return $args;
function wpdrift_filter_posts_clauses( $args ) {
	global $wpdb;
	if ( ! strstr( $args['join'], 'wc_product_meta_lookup' ) ) {
		$args['join'] .= " LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON $wpdb->posts.ID = wc_product_meta_lookup.product_id ";
	$args['orderby'] = 'wc_product_meta_lookup.stock_status ASC, ' . $args['orderby'];
	return $args;

PS: All your products must be using the “Managing Stock” option in the product edit page > Product Data > Inventory.