WooCommerce provides some coarse control over displaying sub categories in your shop/category pages with the “Show subcategories on category pages” and “Show subcategories on the shop page” options on the WooCommerce > Settings > Catalog > Catalog Options section. These controls are all-or-nothing though: show all subcategories on the category pages, or show all subcategories on the shop page. If you want finer-grained control, for instance displaying only certain subcategories on the shop or particular subcategories on a given category catalog page, you’ll need to write a little custom code, and there are a couple of approaches you can take.
The existing limited controls, found in WooCommerce > Settings > Catalog, which allow you to hide all or none of the subcategories:
To clarify what we’re talking about doing here, the goal is to hide for instance only one highlighted subcategory ‘WordPress Plugins’, leaving ‘Magento Extensions’ in the example below:
get_terms Filter
Perhaps the easiest and most upgrade-friendly is to hook onto the core ‘get_terms
‘ filter. This filter is ultimately applied by a call to get_categories()
which is called by the woocommerce_product_subcategories()
template function, which is responsible for returning the product subcategories to be displayed on the shop/category pages. The approach is to hook into that filter, test for the taxonomy/page we are interested in, and alter the returned categories:
add_filter( 'get_terms', 'get_subcategory_terms', 10, 3 ); function get_subcategory_terms( $terms, $taxonomies, $args ) { $new_terms = array(); // if a product category and on the shop page if ( in_array( 'product_cat', $taxonomies ) && ! is_admin() && is_shop() ) { foreach ( $terms as $key => $term ) { if ( ! in_array( $term->slug, array( 'donation' ) ) ) { $new_terms[] = $term; } } $terms = $new_terms; } return $terms; }
The above code will exclude the ‘donation’ category from the shop page. Excluding other categories is as simple as adding to that array. Excluding subcategories from a category page should be as easy as replacing the is_shop()
call with one to is_product_category()
, for all categories, or is_product_category( id|name|slug )
supplying the desired category id or name or slug.
You can put this code as always, in your theme’s functions.php
or a custom site plugin, whichever you prefer.
Some Other Approaches
The nice thing about the above solution is that it does not require overriding any functions or templates. The only real potential drawback to that approach that I can see is that it will indiscriminately apply to any and all calls to get_categories()
which are made for product categories when those is_shop()
, is_product_category()
conditional query tags are active. A more selective solution could be to override one of: woocommerce_product_subcategories()
template function, or the content-product_cat.php
template.
Template Function
The woocommerce_product_subcategories()
template function can be found in woocommerce/woocommerce-template.php
, and could be overridden by copying the function to your theme’s functions.php
and modifying to include a snippet like the following:
... $args = array( 'child_of' => $product_category_parent, 'menu_order' => 'ASC', 'hide_empty' => 1, 'hierarchical' => 1, 'taxonomy' => 'product_cat', 'pad_counts' => 1 ); // start of modification if ( is_shop() ) { $args['exclude'] = "95,96"; } // end of modification $product_categories = get_categories( $args ); ...
Where $args['exclude']
is set to a comma-delimited list of category ids to exclude.
Template
Another approach would be to override the woocommerce template file woocommerce/templates/content-product_cat.php
by copying it to your-theme/woocommerce/content-product_cat.php
and adding something like the following to the top:
if ( is_shop() && in_array( $category->slug, array( 'donation' ) ) ) { return; }
Obviously substituting in whichever subcategories you want excluded, and using the appropriate conditional tags to apply to the particular pages you want.
Hi Justin,
Just wondering if there is the ability to limit what state the user can pickup their order from by their billing address.
Currently running a store here in Australia, and customers often select the “Local Pickup” option by accident when they’re in a different state. I’m looking for a way to stop this from happening.
George.
Hey George, thanks for the question. I don’t have a perfect answer for you, not without some custom code at this point anyways. I do have an admittedly self-serving suggestion of something that perhaps might help with the confusion, and cut down on people incorrectly selecting “Local Pickup”, and that is to give my paid WooCommerce Local Pickup Plus shipping extension a try. It allows you to specify one or more local pickup location addresses for your customers to choose from when checking out. Perhaps seeing, or having to select a pickup address would make the checkout process more clear for your customers? Additionally the selected pickup location will appear on their receipt, and on your order page, so there are additional benefits for merchants offering a pickup option. Sorry I don’t have an exact answer for you at this point, but hopefully this helps for now.
Thanks for the prompt feedback mate, I was thinking that plugin might help – and I think i’ll give it a try!
Warm Regards,
George.
You got it 🙂 Feel free to post back here if the plugin works out for you. Or even if it doesn’t, and how it could be improved. I’ve added the billing address check to my list of feature requests, so that’ll get in there at some point as time allows. All the best
It would be great if local pickup was only offered as an option to customers in certain zip codes and if a handling fee could be added. (much like local delivery is handled)
BTW – thanks for posting code snippets – very helpful
PS on my last comment – I just realized all I need to do is change the title the customer sees for local delivery to “local pickup” and use that. Duh!
Thank you
Hey Kathy, thanks for the feedback. Your right, a good way to handle this is to alter the title; but still what you describe would be a nice feature to have, so I’ve added it to my internal feature request tracker, I’ll do my best to get it into a future release. Thanks again!
Hi thanks for your workarnoud. But it only works on a full shop view. When you put a shortcode: [product_categories number=””] on a random page your solution doesn’t work anymore.
Is there a way to exclude sub-categories for this shortcode?
Not sure off the top of my head what the best solution would be, I think it’s difficult to detect when you’re being called from within a shortcode. Perhaps trying out my template approach that I describe at the end of the article, and just removing the
is_shop()
check to have it apply everywhere?I know this is extremely old, but I’m just confirming that getting rid of is_shop() worked.
Hi! Do you know how to exclude products in some categories from main shop page?
Hey romapad, I couldn’t find any configuration options within the WooCommerce settings to accomplish this, so I bet you could follow an approach similar to what I’ve outlined above, only working with products rather than categories. It looks like it will take some custom coding in any event.
Hey Justin,
I just spent about 20 minutes reading through your articles. Good work!
Question hoping for solution.
When I set up categories, woocommerce shows them 1 category per row on the category listing page.
Do you know of a way to set this up to show 4 categories per row instead of just 1?
Hey Robert, glad you found the articles useful. Categories should be 4 per row, or the same as products. Perhaps it’s the theme you’re using?
Thank you, worked perfectly 🙂
You’re welcome, glad it worked!
First of all, thanks for sharing your knowledge with us all.
I tried your solution but i noticed that the category name also disappeared from the products categories widget on the shop page. I had a solution working that only removed the products from a said category to show up on the shop page but it still listed the category on the widget (im using collapsing categories plugin to have the toggle effect) inside the shop page. The problem was, it also removed the products from showing up on the admin.
But reading your article made me realize the last piece of my solution: creating a if condition !is_admin to prevent excluding the category from showing up on the administration panel. Anyway, heres the code:
http://pastebin.com/R6h6VT0t
Hey Pedro, yeah that was one of my fears with my solution, is that the categories could disappear from unexpected places. Glad to hear you were able to take something useful from the article though and get your solution working 🙂
hi
thanks for a wonderful tutorial
I am wondering how to exclude the categories from just appearing on the main static front home page?
thanks mark
Hey Mark, you can try the above code examples, but using the
is_front_page()
checkyou can instead exclude all categories except “example, example 2, example 3” by modifying the file content-product_cat.php?
Yup Mario, you can definitely do that with the right conditional check
you might have about the instructions?
Well, if you’re doing it by category name rather than slug, you could do:
if ( ! in_array( $category->name, array( 'example', 'example 2', 'example 3' ) ) ) {
return;
}
Assuming those are your category names names, put that at the top of the your overridden
content-product_cat.php
file as described in the last section of the post above, to exclude all but those categories.thanks for the reply! actually I’m confused. I would ecludere all product categories except for certain: namely 11. Could you be more clear about it?
Right, so list out the names of the 11 categories you want to include, replacing ‘example’, ‘example 2’, ‘example 3’ in my previous code snippet
if ( is_home() && if ( ! in_array( $category->name, array( ‘elettronica-2’, ‘modding’, ‘gaming’ ) ) ) {
return;
}
right?
those look more like slugs, so try:
if ( ! in_array( $category->slug, array( 'elettronica-2', 'modding', 'gaming' ) ) ) { return; }
thank you very much indeed. I’ve been a big help. thank you!
I have been trying to find out a way to exclude a particular category called featured from my WooCommerce shop front page which displays by show subcategories.
Finally
I found the code
( ! in_array( $category->slug, array( ‘brands-en’, ‘cellular-phone-en’, ‘samsung-phones-en’ ) ) ) { return; }
I test your differents codes but not working:
in is_shop() or is_front_page() or !
On functions.php, I test all conditions but I not hide the subcategories
Thanks for the write up but I’ve exhausted Method 2 (with slug) and 3 (with ID) without any changes to the Product Category sidebar, nor the Product itself – nothing to the SHOP.
Everything is as per identical to previous. Anything I am missing here? In fact, I have tried to load my site without these 2 files:
content-product_cat.php
woocommerce-template.php
Once I click on SteveJobs.com/shop > All Product Categories + Product (supposed to be excluded) are still showing.
As for Method 1, can you be more precise on how to change the Taxonomy? I re-read the whole paragraph for more than 10 times now, and I am still unsure on the insertion of the code …. at which file? Via FTP, .CSS?
WP 3.4.1 Woocommerce 1.6.1
Hey, sorry if the article wasn’t particularly clear in some of the details, my bad. I’ve since updated it to include some images of exactly what we’re trying to do here, and made it more clear where certain code goes.
If you take a look at the images, you’ll see it wasn’t actually the product category sidebar that I was trying to alter.
For method 1, putting that code in your theme’s functions.php would work just fine, and you could do that via FTP. Hope this helps
Hi Justin,
I have a slightly different problem but I think the function you described above maybe could help me if adjusted a little.
I would like to have the woocommerce page navigation only shown on my main shop page and not on the category pages. Any idea how I can remove the pagination from my category pages?
Thanks!
Hi, I tried all 3 methods and NONE of them worked … !
I juste need to remove my special deals category from the shop page, which seems pretty simple but strangely none of the solution I tried seem to have the slightest effect.
Hey David, sorry to hear that, are you certain you used the correct category id or slug?
I needed to hide subcategories within a particular subcategory based on a user meta field I added to each profile. The user meta field lists all the subcategories under a particular parent and a checkbox controls access to each of them.
To decide which to show and which to hide I tried using the get_terms filter approach but had some issues. I ended up using the woocommerce filter ‘woocommerce_product_subcategories_args’. Within that I checked to see if the user was logged in and then based on that either remove all but a sample subcategory or just the ones the currently logged in user didn’t have access to.
It works great and is another method I’d suggest for handling the hiding of certain subcategories that doesn’t require hacking woocommerce templates.
That’s a great tip Scott and a very handy filter. Looks like that got added in WC 2.0, cool!
This is maybe little off topic, but I have question. I did a function which change product visibility.
add_filter( ‘woocommerce_product_is_visible’, ‘myWoocommerce_product_is_visible’, 10, 3 );
function myWoocommerce_product_is_visible( $visible,$prod_id) {
……
return $visible;
}
My problem is that now the pagination does not match the real product numbers. How can i regenerate pagination?
Hey Gorazd, well for what you’re talking about you’ll need to hook in and modify the main page query, which can be sort of a challenge. If you get it working, you won’t even need to use the
woocommerce_product_is_visible
filter thoughHello,
I was wondering if there is a way to hide certain categories from certain users/roles? For example:
I have six categories…
1. Desert Dips
2. Dessert Dips Wholesale
3. Olive Oil
4. Olive Oil Wholesale
5. Salsa
6. Salsa Wholesale
I want to only show the Wholesale categories to a “Wholesale” user role and hide the “Wholesale” category from all other users and/or roles.
Any information will be much appreciated.
Cheers,
~D
Hey Dan, well there is the Groups for WooCommerce plugin http://www.woocommerce.com/products/groups-woocommerce/ which should get you pretty close, but I’m not 100% sure about the categories thing. Unfortunately I don’t think there’s any “easy” option for what you’re asking, even though it’s definitely a pretty common use-case. Best of luck
Great write up, thanks for the info. Ive decided to take the “override the woocommerce template file” approach and it works great. I modified my template file even more to remove the images as well. What I cant figure out though is how to have the categories in a list above or beside the product loop. Right now my categories display at titles but they are in the product loop and are all spaced out through out the product grid. I just want a nice tight organized list. Any ideas?
I was able to get categories to show outside of the loop by adding the code below to my functions.php file however it only works when the “Show subcategories on the shop page” box is checked in the catalog settings page. I feel like im in the right direction.
add_action( ‘woocommerce_before_shop_loop’, ‘woocommerce_product_subcategories’, 10 );
function woocommerce_show_categories($cat_args){
$cat_args[‘hide_empty’]=0;
return $cat_args;
}
Hey David, sounds like you’re on the right track. You could also look into perhaps using the “WooCommerce Product Categories” widget, whether directly or as a reference. Or if you need total control, you could implement your own thing with the WordPress Walker class
I am trying to make a new secondary shop page where the user can add 4 products (No less, no more) to his cart from a list of several dozens of products of a specific subcategory (which will be excluded from the main shop page) and then only I am forwarded to the cart. What is your preferred way to achieve this.
I also want to make those 4 products be called 1 bundle and if I remove any one of those from cart, other 3 are also removed.
Any ideas?
thanks
Best way to accomplish this is with a custom product archive template and using an extension like Product Bundles. good luck!
Hi, I can’t even see the option to turn off “show sub categories on the shop page” – has this option been removed from the most recent Woo Commerce download? (I have version 20.0.13
It’s still there, it’s just been changed to a dropdown – http://cl.ly/image/320x3h0I1D08
Hi Justin,
I have a problem. I want to print woocommerce product subcategory only. But i am getting all categories. How can i get only subcategories. My code is–
ID, ‘product_cat’, ‘hide_empty=0’ );
foreach ( $terms as $term ){
$category_id = $term->term_id;
$category_name = $term->name;
$category_slug = $term->slug;
echo ‘slug, ‘product_cat’) .'”>’.$category_name.’‘;
}
?>
Where to add this code..? in my function.php..? I want to remove subcategory from the permalink ..
Yep, in your theme’s functions.php should work.
This solution didn’t work for pages where you are viewing a single product. I’m sure there is a way to single out those pages, but I found the easiest (and least intrusive) option would be to just inspect the class name of the product category you want to hide (in my case it was called .cat-item-23), and just put “display:none” in the CSS.
Glad you found a work-around!
Could you please help me change the default category names ordering on the homepage?
Hi Joseph, I believe you can change the order by going to the categories page and dragging / dropping into the order you prefer, or check out a couple of the snippets here: http://wordpress.org/support/topic/plugin-woocommerce-trying-to-order-categories
Hi there,
I’ve developed a plugin that may help non coders to do this:
http://www.wpworking.com/product/woocommerce-exclude-categories-pro/
All the best!
Hey Alvaro,
Thanks for making the plugin. $1 is a great deal 😉
Installed plugin; It works, but with:
‘Warning: in_array() expects parameter 2 to be array, null given in..’
http://cl.ly/image/2c1S003z0U20
Errors disappear after selections are marked. Bug?
you don’t need anything just put this format of the shortcode it will work properly but you must read the shortcode parameter what it work for what…..
[product_categories number=”12″ parent=”0″ ids=”1,2,3] here parent 0 mean only top level category will work that time the ids will work otherwise ids wont work for specific category catalog…. i hope this will help if not then read this area
http://docs.woocommerce.com/document/woocommerce-shortcodes/
Hi Justin, using your last two code examples, can the code be changed to hide ALL categories? My goal is to not show any categories on the shop page, but display all categories on the sidebar. Thanks.
Hi there, if you only want to show products rather than categories, you can do this with the built-in Product settings – they’re under WooCommerce > Settings > Products:
If you set them to only show products, you’ll no longer have categories displayed on your shop page. You can then use the WooCommerce Product Categories widget for your list of categories in the sidebar.
Hi Beka, thanks for your reply.
This code (option 3 in the post) works perfectly for what I want to do: remove categories AND products from shop page (my categories still appear on the sidebar, as designed):
if ( is_shop() && in_array( $category->slug, array( ‘donation’ ) ) ) {
return;
}
However, I have about 20 categories and would like to either
(1) hide ALL categories/products on the shop page ; or
(2) list all my categories at one time (for example, the code doesn’t work if I try to enter 2 categories, like ‘donation, pledge’
Is either possible?
Okay, if you want to list categories and show products with their respective categories, we have a plugin for that: https://www.skyverge.com/product/woocommerce-nested-category-layout/
If you just want to show a list of categories, you’ll need to override the shop template (https://github.com/woothemes/woocommerce/blob/master/templates/archive-product.php) and create your category list instead of listing products. This would be a simple job to farm out to someone like Codeable.io or Elto.com if you don’t want to tweak that template yourself.
Hi,
Thank you for this code…I have been trying to hide a sub category with no luck. I tried using all of the different ways with no luck. I did manage to get a little life out of the get_terms filter. However I am getting a weird response. I have the code below in the functions template and the sub category does not show on the website when I am logged in, but it does show when I log out. That is not what I need. I need it to not show at all no matter who looks at the website or if they are logged in or not.
if ( in_array( ‘product_cat’, $taxonomies ) && ! is_admin() && is_product_category(‘shop-gifts-2’) ) {
foreach ( $terms as $key => $term ) {
if ( ! in_array( $term->slug, array( ‘mothers-day’ ) ) ) {
$new_terms[] = $term;
}
}
$terms = $new_terms;
}
return $terms;
}
I have tried changing that code many different ways with no luck…what could I be doing wrong?
Any help would be much appreciated.
Thanks,
Steve
Hmm, unfortunately I’m just not sure. I have to say that it looks right, but the fact that it works only when you’re logged in is very odd indeed, I can’t explain that. Sorry!
As per an earlier poster who unfortunately did not receive a working solution, I also would like to be able to hide items in my separate category “Distro Titles and Used Items” from the main shop page. Customers would need to click on the sub menu “Distro Titles and Used Items”.
The earlier poster wrote:
“Hi! Do you know how to exclude products in some categories from main shop page?”
Best regards,
Richie
This snippet will point you in the right direction.
Great tips! That snippet got me half way. Any ideas for a more comprehensive approach? Goal is to hide ALL products and categories from the Shop page only, for all user types. Prefer doing so with functions.php additions. Thanks!
Thanks very much! Greatly appreciated!
Hey, thanks for the great explanation. I would like to know if it is possible to hide all subcategories when I’m on Depth 1 and Depth 2. I have 4 layers of categories. I’d like to hide categories just depth 1 and 2. It should show for depth 3 and 4. Cheers.
Hey Ruben, one way would be to use one of the approaches above, but instead of using
is_shop()
to apply the change everywhere, you could useis_product_category('category-name')
to have it only applied when you’re on your top-level categories.Hello
I used the code to remove subcategories from a category pages ( is_product_category() )
and it only removed from the category pages not from the shop page. so i used this code twice..
but got an error.
How can i exclude some categories from both the shop page and the other category pages ?
Thanks for the code!
Hey Moshe, you can’t use the same function name twice, so you’ll want to give your functions two different names and you should be good to go.
Hello Justin,
I’m trying to ‘filter categories by product tag’. So I have a tag list that I click on which loads the archive_product.php page and lists products by whatever tag I clicked – like: http://www.example.com/product-tag/the-tag-name/. The issue is that I only want to show categories that have products with that product tag and not all categories. I am not very familiar with woocommerce, so I’m wondering if this is possible.
Hey Jonathan, almost anything is possible, but this would require a decent amount of code :). It sounds like you’re trying to do some advanced filtering – have you checked out something like FacetWP instead? It might be easier to just use something like this.
Any suggestions for a clean, functions.php way to hide all products AND categories from the Shop page?
Hey Scott, why not just hide the shop page? What would you be using it for then? You can set the visibility to hidden.
Hey Beka, we have been using shop page for awhile, and this is a redesign. Only selling one product, but in three ways, so will use a plan table on shop page, to link to each product page. If a buyer adds to cart from product page, and then removes from cart, the return to shop button sends them to the shop archive (or whatever page I assign as archive in Woo settings). The shop archive shows products and categories, which just clutters the mess. I suppose we could have products belong to no category, but it does carry a useful SEO benefit by way of slug. I got the products off, with that snippet you posted above, but the category thumbnail’s still there. Thanks in advance for your suggestions.
Hey Scott, the easiest thing to do would probably be to change the “return to shop” button then (sample snippet here). You can instead direct this wherever you’d like. If not, overriding the shop template (product archive) would be the most straight-forward thing to do instead rather than trying to unhook every action via your functions.php.
A follow up…by removing categories from products, the category thumbnail is no longer on shop page, but now the products have returned, and setting Shop Archive to show subcategories only (instead of products) doesn’t help.
Thanks for that advice Beka. Sounds like a good long term solution. For now, I have removed the assigned categories from the products, set woo to show products not categories, then used CSS to hide them (display:none) from shop page.
Hiding the shop page, that is what i am looking for. Is that possible? The only way I am thinking of is to remove it from the menu or edit the name/permalinks instead of “shop” to “nobodyknowsthis” for example OR you can make a redirect from shop to homepage. What would you choose or do you maybe have something else in mind?
A redirect would probably be your best bet since WooCommerce will use the shop link in several places, such as the “Return to Shop” button from the empty cart for example.
Hi Justin. Is this post usefull to hide subcategory products from main categories?
Hi guys!
I hope you can help me.
I’d like to collect two particular groups of products, each one belonging to a specific category, in a third category.
I’d like to HIDE that third category from the loop, I mean from the shop page and from all other woo pages. I need to hide it also from the list of category a product belongs to.
Do you think it’s possible to do that with a snippet?
Thanks in advance.
I think this solution is a couple of years old and none of the original suggestions and cod seems to work.
My client just wants to have a few active products that are hidden from the Shop page that he can selectively send the page link to for his customers to purchase.
Can this be done with Woocomm?
Yep – they can be set to have a hidden visibility so they’re hidden from all shop loops: http://docs.woocommerce.com/document/managing-products/#section-3
Hey Justin!
Is there a way to strip out only the title from category pages?!
I have something like: Cat1 | Cat2 | Cat3 | HomePageTitle
I want to get rid of the HomePageTitle. How would I do that?
Thanks,
-Amin
Hi all,
This post and thread seems pretty old at this point but I am just setting up a WooCommerce Shop and was wondering if anyone knows a solution to add a button that shows/hides the “subcategories” when you’re displaying “both” within a main category in the shop? …or if it is easier, to simply separate them with a horizontal line and/or title saying “products” and “subcategories” ? I can’t figure out the second option with just CSS because .product class refers to both and they are all displayed inline.
Thanks!