Keep Selected Products At The Top Of Your WooCommerce Shop
You have a new product you want people to notice, but it keeps getting buried in the shop. This snippet lets you choose what comes first in your shop by adding a few product IDs. The rest of the listing keeps its usual sorting.
What Happens When Someone Sorts By Price?
The pinned products stay ahead of the others. Choose “price: low to high” and WooCommerce sorts the pinned products by price first, then the remaining products by price. That means an expensive pinned item can still appear above a cheaper unpinned one. Use this when that is the behavior you want.
A pinned product still has to belong in the results. It will not appear in an unrelated category or bypass the store's visibility and stock filters.
Here Is The Snippet
Create a folder called stackvane-product-pinning inside wp-content/plugins/. Add a file named stackvane-product-pinning.php inside it and paste in this code. Replace the example IDs before activating StackVane Product Pinning from the Plugins screen.
If you already use a PHP snippet manager, you can add it there instead. Leave out the opening <?php if the editor supplies it. Install it once, using either method, and try it on a staging store first.
<?php
/**
* Plugin Name: StackVane Product Pinning
* Description: Pins selected products in classic WooCommerce catalog queries.
* Version: 1.0.0
*/
defined( 'ABSPATH' ) || exit;
add_filter( 'posts_clauses', 'svhq_pin_catalog_products', 999, 2 );
function svhq_pin_catalog_products( $clauses, $query ) {
if ( is_admin() || ! $query->is_main_query()
|| ! function_exists( 'WC' ) || $query->is_search() ) {
return $clauses;
}
if ( ! $query->is_post_type_archive( 'product' )
&& ! $query->is_tax( array( 'product_cat', 'product_tag' ) ) ) {
return $clauses;
}
// Replace these example IDs with your own product IDs.
$pinned_ids = array( 73, 105 );
$pinned_ids = array_values( array_unique(
array_filter( array_map( 'absint', $pinned_ids ) )
) );
if ( ! $pinned_ids ) {
return $clauses;
}
global $wpdb;
$ids = implode( ',', $pinned_ids );
$priority = "({$wpdb->posts}.ID IN ({$ids})) DESC";
$existing = isset( $clauses['orderby'] )
? trim( $clauses['orderby'] ) : '';
$clauses['orderby'] = $priority . ( $existing !== ''
? ', ' . $existing : ", {$wpdb->posts}.ID ASC" );
return $clauses;
}
Swap In Your Product IDs
Go to Products → All Products and hover over a product row to find its ID. Replace array( 73, 105 ) with the IDs you want to pin. One product? Use array( 73 ). For a variable product, use the parent product's ID.
The list makes a group of pinned products; it does not give each one a fixed position. Writing 73 before 105 will not force 73 to appear first. The current catalog sorting decides their order within the group.
The Small Detail In The SQL
The snippet adds an IN check before WooCommerce's existing ORDER BY. A matching product gets 1, everything else gets 0, and descending order puts the matches first. The IDs are converted to integers before they enter the query.
You may have seen this done with FIELD(ID, 73, 105) DESC. That puts 105 before 73 because its position in the list is higher. Using IN gives both products the same priority, leaving their order to the shopper's sorting choice.
Which Shop Pages Does It Work On?
This version targets the main query on a classic WooCommerce shop, product category or product tag page. It leaves admin screens, search results and secondary queries alone. Brand and attribute archives are not included.
A Product Collection block, Divi shop module or shortcode grid can fetch products through a different query. The same goes for custom AJAX filters and Store API requests. If your grid uses one of those, this snippet may need adapting to that query rather than pasting in unchanged.
Want it only on the main shop page? Replace the second if block with:
if ( ! $query->is_post_type_archive( 'product' ) ) {
return $clauses;
}
They Will Not Repeat On Every Page
The database sorts the results before splitting them into pages. Pinned products start on page one; they are not added again on page two. If you pin more products than fit on one page, the remaining ones continue onto the next.
Before using it live, switch between price and newest sorting, try a category with no pinned products, and check page two. Check your stock and visibility filters too. If the order does not change, clear the store's caches and check whether another extension is replacing the sorting.
Keep the list short. The extra sorting can add database work on a large catalog, so check page speed with your own products and filters. To turn it off, disable the snippet or deactivate the plugin. It does not change saved product data.
References
- WordPress: posts_clauses filter
- WooCommerce: conditional tags
- MySQL: built-in functions, including FIELD()