How to set different sorting rules for different WooCommerce categories?

WooCommerce’s default sorting dropdown is a cluttered mess. Here’s what it usually looks like:

If you just want the quick fix, scroll straight to the bottom for the final code block.

Stop messing with the labels only

If you just want to remove or rename options in that dropdown, you’d think hooking into woocommerce_catalog_orderby is enough. You can drop this into your functions.php:

add_filter( 'woocommerce_catalog_orderby', 'r802_lab_update_sort_dropdown_labels_and_order' );

function r802_lab_update_sort_dropdown_labels_and_order( $options ){

    unset( $options[ 'popularity' ] ); // Sort by popularity
    unset( $options[ 'menu_order' ] ); // Default sorting
    unset( $options[ 'rating' ] ); // Sort by average rating
    //unset( $options[ 'date' ] ); // Sort by latest
    //unset( $options[ 'price' ] ); // Sort by price: low to high
    //unset( $options[ 'price-desc' ] ); // Sort by price: high to low
    $options[ 'price' ] = 'Sort by price xxx'; // rename

    return $options;

}

Here’s the reference for those internal slugs:

  • popularity – Sort by popularity
  • menu_order – Default sorting
  • rating – Sort by average rating
  • date – Sort by latest
  • price – Sort by price: low to high
  • price-desc – Sort by price: high to low

The problem? That filter only changes the UI labels. If you rename ‘Sort by latest’ but don’t change the underlying query, your products will still load in the original default order when a user hits the page, even if the dropdown says otherwise.

The user is stuck with the wrong view until they manually toggle the sort—a massive UX failure.

The actual way to force the sort order

To change how the products are actually queried, you have to hit woocommerce_get_catalog_ordering_args.

add_filter( 'woocommerce_get_catalog_ordering_args', 'r802_lab_force_default_sort_by_latest' );
function r802_lab_force_default_sort_by_latest( $args ) {

    // This only takes effect when the user has not manually selected a sort order, i.e., when the URL in the address bar does not end with a suffix like /?orderby=price.
    if ( ! isset( $_GET['orderby'] ) ) {
        $args['orderby'] = 'date';
        $args['order']   = 'DESC';
    }

    return $args;
}

Now, when you land on the page with a clean URL, the sorting actually works as intended.

Need specific rules per category?

If you want to get granular—like setting different default sorts and dropdown options for individual categories—do this:

// Control the dropdown labels per category
add_filter( 'woocommerce_catalog_orderby', 'r802_lab_category_orderby_text', 99 );
function r802_lab_category_orderby_text( $options ) {
    if ( is_product_category( 'shirts' ) ) {
        return array(
            'date'       => 'Newest Arrivals',
            'price'      => 'Sort by price low to high',
            'price-desc' => 'Sort by price high to low',
        );
    }

    if ( is_product_category( 'accessories' ) ) {
        return array(
            'title'      => 'Sort alphabetically',
            'price'      => 'Sort by price low to high',
            'price-desc' => 'Sort by price high to low',
            'date'       => 'Newest Arrivals',
        );
    }
    return $options;
}

// Force the actual query to match your custom labels
add_filter( 'woocommerce_get_catalog_ordering_args', 'r802_lab_force_custom_orderby', 99 );
function r802_lab_force_custom_orderby( $args ) {

    if ( ! isset( $_GET['orderby'] ) ) {
        if ( is_product_category( 'shirts' ) ) {
            $args['orderby'] = 'date';
            $args['order']   = 'DESC';
        } elseif ( is_product_category( 'accessories' ) ) {
            $args['orderby'] = 'title';
            $args['order']   = 'ASC';
        }
    }


    if ( isset( $_GET['orderby'] ) && $_GET['orderby'] === 'alphabetical' ) {
        $args['orderby'] = 'title';
        $args['order']   = 'ASC';
    }

    return $args;
}

This code cleans up the dropdowns to show only what matters, renames your labels, and forces the DB query to respect your default sorting logic.

The TL;DR for the lazy

If you want total control, remember the two-step process. Changing the label is useless if you don’t change the execution.

  • woocommerce_catalog_orderby: Use this to hide or rename what the user clicks.
  • woocommerce_get_catalog_ordering_args: Use this to force the database to actually sort the way you promised the user.

Leave a Reply

Your email address will not be published. Required fields are marked *