Show Out of Stock Products Last in WooCommerce

WooCommerce shows out of stock products wherever they fall in the sorting, so a customer can scroll past a row of products they cannot buy before reaching ones they can. Hiding them completely loses the product pages and their search traffic.

This snippet shows out of stock products last in WooCommerce: on the shop, product category and tag pages and in product search. Inside each group the sorting stays whatever the customer chose: default, popularity, rating, latest or price.

The snippet

<?php
/**
 * Show out of stock products after the products in stock, in every shop sorting.
 */
add_filter( 'posts_clauses', 'dpc_out_of_stock_last', 20, 2 );

function dpc_out_of_stock_last( $clauses, $query ) {
	// Only the WooCommerce shop, product category, tag and product search pages.
	if ( 'product_query' !== $query->get( 'wc_query' ) ) {
		return $clauses;
	}

	global $wpdb;
	$clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS dpc_stock ON ( {$wpdb->posts}.ID = dpc_stock.post_id AND dpc_stock.meta_key = '_stock_status' )";

	$stock_order        = "CASE WHEN dpc_stock.meta_value = 'outofstock' THEN 1 ELSE 0 END ASC";
	$clauses['orderby'] = '' === trim( $clauses['orderby'] ) ? $stock_order : $stock_order . ', ' . $clauses['orderby'];

	return $clauses;
}

How it works

  • WooCommerce marks its own catalogue query with the query var wc_query set to product_query. The snippet only touches that query, so admin lists, product blocks, widgets and the cart are not affected.
  • It joins the _stock_status value of each product and puts one rule in front of the existing ORDER BY: out of stock products get 1, everything else 0. WooCommerce’s own sorting (price, popularity, title and so on) follows after it.
  • Products on backorder can still be bought, so they stay with the products in stock.
  • The original snippet by Business Bloomer replaced the sorting with orderby => meta_value on the stock status. That overrode the customer’s choice in the sorting dropdown and only put outofstock last because it sorts after instock alphabetically.

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor and publish. Leave Action/Hook empty. The filter waits for the shop query, so it can load before WooCommerce.

The same code also works unchanged in a small plugin or an mu-plugin.

Customise it

  • Backorders last too: change the condition to dpc_stock.meta_value IN ( 'outofstock', 'onbackorder' ).
  • Hide out of stock products instead: WooCommerce has a setting for it in WooCommerce → Settings → Products → Inventory → Out of stock visibility.
  • Change the label of the products that stay: see Woo : Change “Out of Stock” to SOLD OUT.

Gotchas

  • The [products] shortcode and the Product Collection block run their own queries, not the catalogue query, so they keep their own order.
  • Variable products have the stock status of the parent: a variable product counts as in stock while at least one variation is.
  • Page caches keep the old order until they are cleared.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.