WooCommerce ships a product search, but it searches the whole store. A shopper who has already narrowed to one category and then searches gets thrown back out to every product you sell. The fix is a search form scoped to the category being viewed, which is a hidden field and about thirty lines of PHP.
Why does WooCommerce search ignore the category a shopper is browsing?
Because the default form posts only s and post_type=product, and neither carries the category. WordPress has no way to know the shopper was three clicks into Outdoor Furniture when they typed, so it searches every product. The category is context the form throws away.
WooCommerce does read a product_cat query variable on search results, and it is already registered. Nothing needs adding to make it work. The form simply has to send it.
How do you add a search box that searches inside one category?
Hook a form onto the category archive, guard it with is_product_category(), and pass the current term slug as a hidden product_cat field alongside the search term.
add_action( 'woocommerce_before_shop_loop', 'tmc_category_search_form', 5 );
function tmc_category_search_form() {
if ( ! is_product_category() ) {
return;
}
$term = get_queried_object();
if ( ! $term instanceof WP_Term ) {
return;
}
$label = sprintf(
/* translators: %s is the product category name. */
esc_html__( 'Search in %s', 'your-textdomain' ),
$term->name
);
?>
<form role="search" method="get" class="category-search"
action="<?php echo esc_url( home_url( '/' ) ); ?>">
<label for="category-search-field" class="screen-reader-text">
<?php echo esc_html( $label ); ?>
</label>
<input type="search"
id="category-search-field"
name="s"
value="<?php echo esc_attr( get_search_query() ); ?>"
placeholder="<?php echo esc_attr( $label ); ?>" />
<input type="hidden" name="post_type" value="product" />
<input type="hidden" name="product_cat" value="<?php echo esc_attr( $term->slug ); ?>" />
<button type="submit"><?php esc_html_e( 'Search', 'your-textdomain' ); ?></button>
</form>
<?php
}
Three details in there are not decoration. The is_product_category() guard stops the form appearing on the shop page and on tag archives, where there is no term to scope to. The WP_Term check stops a fatal if the queried object is not what you expect, which happens on some filtered views. And the screen-reader-text label is what keeps the input from being an unlabelled box, which is the single most common accessibility failure we find on ecommerce sites.
Does the search include subcategories?
Yes. A product_cat query includes child terms by default, so searching inside Furniture also returns matches in Chairs and Tables beneath it. If you need to restrict the search to the exact term and exclude children, that is a tax_query with include_children set to false, which means filtering the query rather than changing the form.
Why do the results look different from the category page?
Because they are not rendered by the same template. The category archive uses the WooCommerce product loop, and search results fall through to your theme’s search.php, which usually renders posts rather than product cards. If the results page looks wrong, that is the reason, and the fix is a search.php that detects post_type=product and loads the product loop.
This is the step most snippet posts leave out, and it is where a working search still feels broken to a shopper.
Where should this code go?
A child theme’s functions.php or a code snippets plugin. Never the parent theme’s functions.php, because the next theme update overwrites it and the search silently disappears.
Test it with the search field empty as well as filled. An empty submit sends s= with a category still attached, which some themes render as an empty results page rather than the category the shopper was already looking at.
Is a snippet the right answer for a large catalogue?
Up to a few hundred products, yes. Past that, the bottleneck stops being the form and becomes the query: WordPress search does a LIKE match against post title and content, which does not rank results by relevance and gets slow as the catalogue grows. At that size the honest answer is a search index rather than a bigger snippet.
If a scoped search is one item on a longer list of things your store needs to do properly, that is web design and development work rather than a snippet, and it is worth scoping as such.
References
- get_queried_object(), WordPress developer reference
- get_search_query(), WordPress developer reference
- WP_Term, WordPress developer reference
