Blog

Hide Certain WooCommerce Sub-Categories in Catalog

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:

WooCommerce > Settings > Catalog

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:

Some example subcategories on the shop page

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.


Please note that this post may contain affiliate links. This means that we receive a commission from the merchant if you purchase, which funds more time spent on blog posts. :)

80 Comments

  • George 4 months ago Reply

    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.

    • justin 4 months ago Reply

      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.

      • George 4 months ago Reply

        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.

        • justin 4 months ago Reply

          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

          • Kathy 3 months ago

            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

          • Kathy 3 months ago

            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

          • justin 3 months ago

            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!

  • Marcin 4 months ago Reply

    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?

    • justin 4 months ago Reply

      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?

      • Peter 3 months ago Reply

        I know this is extremely old, but I’m just confirming that getting rid of is_shop() worked.

  • romapad 4 months ago Reply

    Hi! Do you know how to exclude products in some categories from main shop page?

    • justin 4 months ago Reply

      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.

  • Robert 4 months ago Reply

    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?

    • justin 4 months ago Reply

      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?

  • Roleat 4 months ago Reply

    Thank you, worked perfectly :)

    • justin 4 months ago Reply

      You’re welcome, glad it worked!

  • Pedro Nave 4 months ago Reply

    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

    • justin 4 months ago Reply

      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 :)

  • mark 4 months ago Reply

    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

    • justin 4 months ago Reply

      Hey Mark, you can try the above code examples, but using the is_front_page() check

  • Mario 4 months ago Reply

    you can instead exclude all categories except “example, example 2, example 3″ by modifying the file content-product_cat.php?

    • justin 4 months ago Reply

      Yup Mario, you can definitely do that with the right conditional check

      • Mario 4 months ago Reply

        you might have about the instructions?

        • justin 4 months ago Reply

          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.

          • Mario 4 months ago

            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?

          • justin 4 months ago

            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

  • Mario 4 months ago Reply

    if ( is_home() && if ( ! in_array( $category->name, array( ‘elettronica-2′, ‘modding’, ‘gaming’ ) ) ) {

    return;

    }

    right?

    • justin 4 months ago Reply

      those look more like slugs, so try: if ( ! in_array( $category->slug, array( 'elettronica-2', 'modding', 'gaming' ) ) ) { return; }

      • Mario 4 months ago Reply

        thank you very much indeed. I’ve been a big help. thank you!

  • 007 3 months ago Reply

    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

    • justin 3 months ago Reply

      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

  • John Keenan 3 months ago Reply

    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!

  • David 3 months ago Reply

    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.

    • justin 3 months ago Reply

      Hey David, sorry to hear that, are you certain you used the correct category id or slug?

  • Scott 3 months ago Reply

    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.

    • justin 3 months ago Reply

      That’s a great tip Scott and a very handy filter. Looks like that got added in WC 2.0, cool!

  • Gorazd 3 months ago Reply

    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?

    • justin 3 months ago Reply

      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 though

  • Dan 3 months ago Reply

    Hello,

    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

    • justin 3 months ago Reply

      Hey Dan, well there is the Groups for WooCommerce plugin http://www.woothemes.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

  • David 3 months ago Reply

    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?

    • David 3 months ago Reply

      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;

      }

      • justin 3 months ago Reply

        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

  • purdue 3 months ago Reply

    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

    • Max Rice 3 months ago Reply

      Best way to accomplish this is with a custom product archive template and using an extension like Product Bundles. good luck!

  • Jerry 2 months ago Reply

    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

  • deepak 2 months ago Reply

    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.’‘;
    }
    ?>

  • Asif Mushtaq 2 months ago Reply

    Where to add this code..? in my function.php..? I want to remove subcategory from the permalink ..

    • Max Rice 2 months ago Reply

      Yep, in your theme’s functions.php should work.

  • Lucas 2 months ago Reply

    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.

  • Joseph 2 months ago Reply

    Could you please help me change the default category names ordering on the homepage?

  • Alvaro 1 month ago Reply

    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!

    • Samantha 1 month ago Reply

      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?

  • mamun 1 month ago Reply

    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.woothemes.com/document/woocommerce-shortcodes/

  • Jumpringer 28 weeks ago Reply

    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.

    • Beka Rice 28 weeks ago Reply

      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.

      • Jumpringer 28 weeks ago Reply

        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?

  • Steve 26 weeks ago Reply

    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

    • Justin Stern 25 weeks ago Reply

      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!

  • Richie 23 weeks ago Reply

    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

    • Beka Rice 23 weeks ago Reply

      This snippet will point you in the right direction.

      • Scott 4 weeks ago Reply

        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!

  • Richie 23 weeks ago Reply

    Thanks very much! Greatly appreciated!

  • Ruben Meibergen 22 weeks ago Reply

    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.

    • Justin Stern 19 weeks ago Reply

      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 use is_product_category('category-name') to have it only applied when you’re on your top-level categories.

  • Moshe 18 weeks ago Reply

    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!

    • Beka Rice 17 weeks ago Reply

      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.

  • jonathan 9 weeks ago Reply

    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.

    • Beka Rice 9 weeks ago Reply

      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.

  • Scott 4 weeks ago Reply

    Any suggestions for a clean, functions.php way to hide all products AND categories from the Shop page?

    • Beka Rice 4 weeks ago Reply

      Hey Scott, why not just hide the shop page? What would you be using it for then? You can set the visibility to hidden.

      • Scott 4 weeks ago Reply

        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.

        • Beka Rice 4 weeks ago Reply

          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.

      • Scott 4 weeks ago Reply

        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.

      • Scott 4 weeks ago Reply

        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.

  • Idromet Sider 3 weeks ago Reply

    Hi Justin. Is this post usefull to hide subcategory products from main categories?

Submit a Comment