I have used custom product types a couple times in the past for different projects where it made sense (for example for a product rental plugin I created both a ‘simple rental’ and ‘variable rental’ product). It is only recommended to create a custom product type when it is really necessary and it doesn’t fit in the existing options of the current product types.

Getting started

In this post I will be using some examples of how to create a Simple Rental and Variable Rental product types. This will be packed in a small and overly simple plugin.

Register a new class

To get started you first need to create a class that is extending the existing WC_Product class. It is vital to have the product class named the same as in the classes’ parameter product_type, (‘same’ as in same name, each first letter of the class name should be capitalised and should have underscore separators). It is vital because WooCommerce uses the product type to get the class name and if its called differently it will not be recognised.

In my example I called my class WC_Product_Simple_Rental.

On itself, this doesn’t do much as you will not see it in the drop down when you select a product type. To add it do the product type drop down, use the following code.

Thats it!, the most basic form of adding a new product type! You will now be able to select your new product type from the drop down on the edit product pages.woocommerce-custom-product-type

 

In the rest of the post I will show a couple examples how you can further extend your own custom product type.

Regaining the price fields for a custom product type

One of the first things you might notice, is that the price fields are no longer available for your custom product type. Although it isn’t relevant for the Rental product types, it may be for your situation. Unfortunately there is no ‘official’ way of regaining those fields (via hooks for example), so we have to manually display them via a bit of Javascript/jQuery.

Adding a new settings panel for your custom product type

If you examine the code above, you will see that a class name show_if_simple_rental is being added, WooCommerce automatically shows or hides certain elements on the page that have related classes. A couple examples of available HTML classes that you can add to elements;

show_if_simple / hide_if_simple / show_if_variable / show_if_downloadable / hide_if_virtual

Each product type (also virtual and downloadable options) has its own classes to show or hide elements.

You can use the following code to add a new settings panel and it will only show for the new Simple Rental and Variable Rental product types.

WooCommerce will auto-load the values of the new custom fields if the id and the post meta field name (key) are the same.

Hiding an existing data panel for your custom product type

Good chance you will not need all the data panels for your new product type, and under the motto “less is more” it is a good idea to remove the unnecessary data panels. With the following code the “Attributes” data panel will not show for the custom product types we just created.

End result – a small custom product plugin

This is my end result when creating this post and posting the code snippets. It will work, but its a plugin in its utter most basic form. That is why its highly recommended to use this as example work, and not actually use it on your website.

Last words

I would love to hear from you if you found this useful, would like to see something else, missing something or just a simple thank you 🙂

Jeroen Sormani

I'm a professional WordPress plugin developer on a mission to create the best plugins for my clients. I'm specialised in developing general WordPress, WooCommerce and Easy Digital Downloads plugins.

Interested in talking about a WordPress project? Get in touch!

Follow me on Twitter

82 thoughts on “Adding a custom WooCommerce product type

Brend January 23, 2017 at 11:04 am

Hello Jeroen,

Very useful tutorial, thank you!

Thanks to your tutorial I was able to set up a simple custom product type, amazing!. However, I find myself in need of setting up a variable custom product type. You mentioned in the beginning of the article that the steps include how to do this, but I can’t seem to find it. Am I missing something? If not, would you mind elaborating on how to do that as well? That would be so helpful! 🙂

Thanks in advance

Jeroen January 25, 2017 at 10:18 am

Hi Brend,

Hmm, seems like I indeed missed to into that deeper.
I haven’t exactly done any verification on this, but I believe you should get far with just extending the existing Variable_Product class instead of the Simple_Product.

I’ll be sure to see if I can update this entire post to reflect this.

Thanks for noting 🙂

Cheers,
Jeroen

Carl February 2, 2017 at 7:12 am

Curious, I know this is a little off topic but you seem to have a firm understanding of this. I have an existing product type called Accommodation Bookings. By default, it’s a virtual product. But in my case, I need it to not be virtual. Do you know if you can change whether it’s a virtual product with a simple script? I can edit the changes in the code but if the plugin is updated it would be overwritten. I’d like a better solution but not sure how to go about it.

Jeroen February 2, 2017 at 9:19 am

Hi Carl,

It would depend on the plugin’s code that is used to set the virtual attribute.
Thats not something I’d be able to get to you out of the blue, it’d need to be researched in the plugin’s code first.

Cheers,
Jeroen

Carl February 2, 2017 at 5:45 pm

Not sure if this would help. It contains the class as well as the function:

Carl February 2, 2017 at 5:46 pm

Striped the script from the comment. Here is a link: https://gist.github.com/anonymous/c8e576d4bb31a218bf6a0e7f63ce8dd8

Jeroen February 16, 2017 at 9:55 pm

Hi Carl,

Going from your code you always return false for the product being virtual. What you’d want/need is to investigate how the Booking plugin you’re using is setting it to being virtual by default. Then see if you can reverse/undo that default setting.

Cheers,
Jeroen

Emil February 15, 2017 at 1:40 pm

Hi Jeroen

Thanx for a good guide on setting this up. I have a question for you if you are willing to answer.

I have Woocommerce Pre-Order plugin and want to have the Pre-Order tab visible on my custom product. How can i manage to do that? The tab is visible on Woocommerce “standard” product types.

Jeroen February 16, 2017 at 9:52 pm

Hi Emil,

Its possible to show/hide tabs by using a css class; show_if_variable where ‘variable’ is the product type. You can also use hide_if_variable

Cheers,
Jeroen

Peter February 17, 2017 at 1:56 pm

Hi Jeroen,

Thank you for this excellent tutorial. Everything you have suggested has worked for me.

Although there is still one small problem. The ‘price’ field and the ‘general’ tab both disappear when the product is saved. In other words, when I want to edit the price, I cannot see the ‘price’ field or the ‘general’ tab on the ‘edit product’ page. I actually have to change the product type back to ‘simple product’, and then back to the new ‘custom product type’ to make the ‘price’ field reappear. This is a bit strange.

Is there a way around this? Is there a way to make the ‘price’ field and ‘general’ tab appear when the edit product page first loads?

Also, I know this is beyond the scope of this tutorial, but if you could give me some tips or point me in the right direction. How would I show a different product page for the new custom product type. In other words, I want to hide the ‘add to basket’ button from the ‘product page’, and add an enquiry form instead. Also I need to remove the ‘add to basket’ button from the ‘catalog’ page too. Any ideas?

Many thanks again,
Peter

Jeroen February 17, 2017 at 7:25 pm

Hi Peter,

Under the head “Regaining the price fields for a custom product type” I described how you can get the pricing fields back for custom product types.

Re custom pages: this would indeed be a bit too in-depth for a comment. I’m trying to revise the post soon to include more info about custom product types 😉

Cheers,
Jeroen

Peter February 20, 2017 at 7:04 pm

Hi Jeroen,

Thanks for your reply. But I have already applied the JavaScript fix which you suggested. It works as far as making the ‘price’ field reappear when you select ‘Simple Rental’ from the product type dropdown, but when you reload the product edit page (e.g. when you are coming from the product list page, or when you ‘click’ update to save the simple rental product), the ‘price’ field is no longer showing. It disappears again. Is there a workaround for this problem? Or will it only show when the value of the dropdown changes?

Regarding my other question, I worked out how to remove the ‘add to cart’ button. I had to add a filter to the ‘woocommerce_is_purchasable’ filter. In other words, I had to tell the system that my new ‘custom non-purchasable’ product is not purchasable. This entirely removes the purchasing functionality for those products, not just the ‘add to cart’ button.

Here is the code:
/**
* Make non-purchasable product ‘non-purchasable’ according to WooCommerce
* Add filter to woocommerce_is_purchasable function
*/
function simple_non_purchasable_product_is_not_purchasable ( $is_purchasable, $product ) {

// Need to check class, as checking ‘product_type’ does not work
$class_name = get_class($product);
if($class_name == ‘WC_Product_Simple_Non_Purchasable’) {
$is_non_purchasable = true;
} else {
$is_non_purchasable = false;
}

// if product is non-purchasable
if($is_non_purchasable) {
return false;
}
else {
return $is_purchasable; // normal
}
}
add_filter(‘woocommerce_is_purchasable’, ‘simple_non_purchasable_product_is_not_purchasable’, 10, 2);

Furthermore, to add stuff to the product page for certain product types, you can check the class as above and use ‘hooks’ to add stuff. See this blog post for more info: https://businessbloomer.com/woocommerce-visual-hook-guide-single-product-page/

Many thanks,
Peter

Peter February 20, 2017 at 7:44 pm

P.S.

I have the same problem as Caroline posted above. I have copied and modified here solution, by making sure the JavaScript checks the current product, with an if statement.

Here is the full script, modified:

/**
* Show pricing fields for simple_non_purchasable product.
*/
function simple_non_purchasable_custom_js() {

if ( ‘product’ != get_post_type() ) :
return;
endif;

?>
jQuery( document ).ready( function() {
jQuery( ‘.options_group.pricing’ ).addClass( ‘show_if_simple_non_purchasable’ ).show();

// check which product type is selected
var selectedProductType = jQuery(‘#product-type’).val();
if(selectedProductType == ‘simple_non_purchasable’) {
// Deactivate shipping Link in left Menu
jQuery( ‘.shipping_tab’ ).removeClass( ‘active’ );
// Hide Shipping Panel on load
jQuery( ‘#shipping_product_data’ ).addClass( ‘hidden’ ).hide();
// Activate General Link in left Menu
jQuery( ‘.general_tab’ ).addClass( ‘active’ ).show();
// Show General Panel on load
jQuery( ‘#general_product_data’ ).removeClass( ‘hidden’ ).show();
}
});
<?php
}
add_action( 'admin_footer', 'simple_non_purchasable_custom_js' );

I hope that helps other people experiencing the same problem.

All the best,
Peter

Justin March 1, 2017 at 2:39 am

Great tutorial! Very clear.

Unfortunately, I can’t get it to work for me. I’m creating a ‘Yarn’ product type and have registered the custom product type and registered it in the ‘Product Data’ drop down menu but it doesn’t show up. I’m more of a front end guy so I’m not sure where I’m going wrong. I see people talking about using this in a plugin but I was just wanting to put it in my php-includes/ directory and require_once() it in my functions.php. Is mine not working because it is just being called into functions.php?

Jeroen March 1, 2017 at 9:44 am

Hi Justin,

You can put the code anywhere and include it in your themes functions.php file, it doesn’t really matter as long as its executed on time.

Cheers,
Jeroen

Justin March 1, 2017 at 5:31 pm

Hmm. I got it working about an hour after I commented. On my end, it only worked when I made it into a plugin. Just adding it to the functions.php or including it into functions.php didn’t work, whatsoever.

Thank you for the quick response, though. Now I’m trying to get the General Tab and others showing by default and not disappearing when the product is saved. Thankfully, someone has already commented with a fix. Hopefully that works out.

Thank you again for the excellent article and ‘customer service’ lol.

Justin March 1, 2017 at 8:47 pm

Never mind my other comments. Finally figured it all out. Have this running as a plugin.
This creates a custom product type (‘yarn’, in my case), registers it in the Product Data drop down field. If the current product type is the custom one (‘yarn’, in my case), add a ‘show_if_customType’ (‘show_if_yarn’, in my example) to the desired Product Data tabs and their fields, make them visible with ‘show()’. I tried storing the multiple selectors for the tabs and their fields in an array variable but for some reason it just wouldn’t work for me. So, they’re just literally a list of chained selectors that target the various Product Data tab and field containing elements that get hidden or shown per respective product type.

Here’s the source snippet: http://pastebin.com/vYy57Hth

If anyone can tell me how to make the multiple chained selectors into a working array variable, let me know. I tried it in different scopes and it just wouldn’t jive. If I can make this a really organize and tidy plugin, I’ll publish it on Github or WordPress Plugin Directory.

Joe April 13, 2017 at 6:16 am

It looks like one small change is necessary to get this to work with WC 2.7+. Instead of having the product class set a property named “product_type” to your product type, it needs to implement the method “get_type()”. For example your class would become

	class WC_Product_Simple_Rental extends WC_Product_Simple {

		public function get_type( ) {

			return  'simple_rental';

		}

	}
Tom April 17, 2017 at 1:18 pm

Give this man a medal! 🙂

Tom April 17, 2017 at 1:38 pm

Just a heads up — after trying to figure out why things won’t save in Woo 3.0+ I finally figured it out!

Custom product type class must extend WC_Product class NOT WC_Product_Simple

Lisa April 23, 2017 at 8:54 pm

Hi Tom (and Joe),

thanks a lot, I did these two changes (extend WC_Product instead of WC_Product_Simple and the get_type() function), but unfortunately, I still can’t save my custom products in the admin area (with Woo 3.0+), the product type always jumps back to “simple product” everytime I try to save. Any ideas? 🙁

Tom April 26, 2017 at 6:21 pm

Sorry for the slow reply (only just spotted your reply)

If you did not fix it yet —

@ https://gist.github.com/stirtingale/753ac6ddb076bd551c3261007c9c63a5/revisions#diff-179610a896570d04e25227a886de04a5

Joe’s initial gist is correct (it is called at “init”) but his “End result – a small custom product plugin” gist is wrong. It is called at plugin loaded.

It must be called at init to play nice with Woo 3.0+

Joe’s initial gist is correct (add_action = init) but the “

Lisa April 27, 2017 at 1:28 am

Hi Tom,

thanks for your reply, I finally figured it out. 🙂 I tried every variation of code I found on the internet (changed “plugins_loaded” to “init” too) but nothing worked. My code was something like this (and always worked with previous woocommerce versions):

function register_custom_product_type() {

class WC_Product_Some extends WC_Product {

public function __construct( $product ) {

$this->product_type = ‘some_product’;

parent::__construct( $product );
}
}
}
add_action( ‘init’, ‘register_custom_product_type’ );

With Woo 3.0+ I also needed to change the class name to ‘WC_Product_Some_Product’ in the example above, or else my product type just wouldn’t save anymore (the type always jumped back to ‘Simple Product’ when saving).

Manisha Sharma August 16, 2017 at 7:24 am

How can I have attributes for this custom product as the plugin you provided is not showing attribute tab.

remsu September 1, 2017 at 11:43 am

I have a problem with the “add to cart” not showing up on product page(the custom one). Did anyone solve that ?
Thanks

RGH September 29, 2017 at 9:55 pm

hi.
Thank you for this excellent tutorial!
i use from dooplay theme with post types likes films and tvshowes.
is it possilble change these post type like woocommerce downloadable product?
i will be thanksfull if you send my answer to my email.

Mohsin November 16, 2017 at 1:52 am

Hi,
I have a question. Can we combine two product types . For example i want to add one product of two type rent and simple product . I want to add both rental and sale price in one product and on front end user select from drop down that he want to buy the product or take the product on rent .

In short i want to merge two type of product to create a single type. Because my client do not want to create two different product for rent and sale. He want a single product having option to add rent and sale price.

Jeroen November 16, 2017 at 9:08 am

Hi,

You could have a look at grouped products, they allow one to show two different products.
Alternatively just a custom view for the products would probably be best, shouldn’t be too difficult to create that. I wouldn’t recommend to actually combine two product types into one, just keep it simple and stick with a modified view.

Jeroen

AT-2017 November 21, 2017 at 3:53 pm

Hello Jeroen (I guess, it’s Jero-en)! I was googling and luckily got your blog for WordPress solution. It’s really great and am willing to know about almost the same thing with a slight difference. I’ve worked with PHP few years earlier and can understand the syn-taxes. I am trying to hide the hourly price in the rental products of the booking-and-rental-system-WooCommerce plugin. I can see it isn’t possible to hide the hourly price from the dashboard and was trying to do something in the code. I got two PHP files for it and tried to modify. I know, this is kind of risky and seems like I am missing something to do that (Tried to remove the code that works for the hourly price). The files are – \wp-content\plugins\booking-and-rental-system-woocommerce\templates\single-product\add-to-cart\redq_rental.php and \wp-content\plugins\booking-and-rental-system-woocommerce\includes\class-redq-product-cart.php. I don’t know if I can disable the hourly price feature but if you have gone through this situation, let me know and would be happy to have a solution from you. Thanks.

My Scenario: In a website, when a customer selects the date, like from 20/11/2017 and to 20/11/2017, then it counts hourly price as in the plugin given (In the dashboard precisely) – ‘Hourly price will be applicabe if booking or rental days min 1day’ and this should work as a single day not hourly. But works fine for the general price when the date range is like this from 20/11/2017 and to 22/11/2017. So I just want to enable the date-wise rental price.

Jeroen November 22, 2017 at 10:51 am

Hi,

I’d advise to contact the plugin author for support on that. This post is meant for adding your own custom product type, not support on a different plugin that has done that 😉

Given the folder structure, it does look like the site support WC template system; https://docs.woocommerce.com/document/template-structure/

Regards,
Jeroen

Mahendra Prasath March 6, 2018 at 7:26 am

How to get the custom product type in front
product object returns a product-simple only
could you please let me know how to get the custom product type in front end.

Toby May 12, 2018 at 2:51 am

Thank you very much for this first step tutorial for WC beginners like me. I will try to build my first WC plugin for a friend based on this. Thanks 🙂

Paul September 9, 2018 at 1:42 pm

Hey Jeroen,

I’m setting out to create a new ‘combo’ product type that improves on WC’s own grouped type:

– listing a single price that’s the sum of the component products’ prices, not a price range from the lowest-priced component product to the highest.

– allowing only one quantity to be selected for the combo, not for each grouped product separately.

– grouping not just simple products but also variable products’ variations.

– limiting the inventory of the combo product to the smallest inventory of any of its component products.

– going out of stock when any of its component products goes out of stock.

If all that proves to be too daunting (I’m experienced with WP and PHP but find reverse-engineering the WC source code to be eye-crossing), my fall-back idea is to create a dynamic connection between the inventory of one product and the inventories of two or more other products, so that when product A is purchased then the inventories of linked products B & C are decremented, and vice versa.

I’ll let you know if I’m successful in either endeavor; in the meantime if you have any suggestions beyond what’s in this blog post I’d be grateful for your support.

Thanks for a great post,
Paul

Rachael Portier January 27, 2021 at 7:29 pm

I’ve created a custom product type for discontinued products, which become unpurchasable with no price, and the product type is working perfectly. Except: It seems to be saving information, like price and stock, if the product was originally a simple product before being changed to a discontinued product. Is there anyway to “clear” the info from other product types for this product when it’s changed to a discontinued product type? I hope that makes sense!

Jeroen Sormani January 27, 2021 at 7:49 pm

Hi Rachael,

Its possible to do this, either by a snippet using PHP or JavaScript, either would be capable of doing so.
Unfortunately I don’t have something ready to go to provide for this. Feel free to reach out through my contact form if you want help with this.

Cheers,
Jeroen

Kirti February 19, 2021 at 10:28 am

It’s nice one. But can we add variable custom type product?

Jeroen Sormani February 19, 2021 at 10:44 am

hi Kirti,

Sure thats possible. I haven’t done this myself (yet), but I do expect it would require additional work to ensure the proper variation data is stored (something vaguely comes to mind that Woo does a check to store data such as the variations, so a custom product type may not trigger this).

Carlos De Freitas June 14, 2021 at 9:03 pm

Hi Jeroen,

Thank you so much for this! It is very helpful.
Is there a way to copy or clone the product type created to have other product type with the same characteristics and functionalities but different product name? or do I need to copy all the code twice?
I was thinking something like this:
First product type:
class WC_Product_Simple_Rental extends WC_Product_Simple {…your code…}
Second product type:
class WC_Product_Simple_Rental_2 extends WC_Product_Simple_Rental {
public function __construct( $product ) {
$this->product_type = ‘simple_rental_2’;
parent::__construct( $product );
}
}}

Jeroen Sormani June 15, 2021 at 11:01 am

Hi Carlos,

I haven’t tested it, but I don’t see a reason why it shouldn’t work in the way you’ve exampled.

Cheers,
Jeroen

Alex Bruton September 10, 2021 at 11:26 pm

Hello from Canada!

I wanted to say I’ve really appreciated your posts, e.g.: this one on product types, and this one:

https://jeroensormani.com/adding-custom-woocommerce-product-fields

Thank you!

I also wanted to ask if you have published any guidance on making a new custom product type that is virtual and downloadable by default?

I have had trouble making the checkboxes appear on a new custom type like the one you have outlined here, and setting them to checked by default.

Cheers, and all the best for the weekend!

Alex.

Jeroen Sormani September 11, 2021 at 1:09 pm

Hi Alex,

Glad to hear you enjoyed these posts 🙂

Unfortunately I don’t have something published on that topic. Feel free to reach out through the contact form though if you’d be willing to discuss this further through email. I’d be interested in hearing your use case in general.

Cheers,
Jeroen

Alex Bruton September 11, 2021 at 3:47 pm

Will do. Thanks again!

Leave a Reply