Custom Product Tabs for WooCommerce

Sometimes you need more tabs for your products. Sometimes the descriptions tab, reviews and attributes tab are not enough. You may need a tab with directions on how to use the product, a tab on ingredients of the product or a tab displaying all available sizes. So let’s talk about adding custom product tabs for WooCommerce. How can you add an additional tab to the product page?

WooCommerce Product Tabs Filter

To add a custom product tabs for WooCommerce you can follow WooCommerce’s guidelines and add the following to your functions.php file:

add_filter( 'woocommerce_product_tabs', 'woo_new_product_tab' );
function woo_new_product_tab( $tabs ) {
 // Adds the new tab
 $tabs['test_tab'] = array(
 'title' => __( 'New Product Tab', 'woocommerce' ),
 'priority' => 50,
 'callback' => 'woo_new_product_tab_content'

return $tabs;

function woo_new_product_tab_content() {

// The new tab content

echo '<h2>New Product Tab</h2>';
 echo '<p>Here\'s your new product tab.</p>';

You will of course have to adjust the code to your liking. So you should at least:

  • $tabs label
  • title – pick you own title
  • priority – set the priority of the tab – perhaps it should come first?
  • callback – add the name of the function loading the tab data

So something like:

$tabs['ingredients'] = array(
 'title' => __( 'Ingredients' ),
 'priority' => 10,
 'callback' => 'ianua_product_ingredients_tab'

should do the trick just fine.

Tab Contents Callback Function

Inside the code above we also added the function being the one loading the content in the tab:

function woo_new_product_tab_content() {

// The new tab content

echo '<h2>New Product Tab</h2>';
 echo '<p>Here\'s your new product tab.</p>';

So the filter is followed by the function loading the content. Later on we shall see we did not do the same and used a template tag woocommerce_get_template to load the code from another file.

Content to Load

What would you like to load in the tab and how would you like to display it? For that you need to create a function and add it to your functions.php file or include it somehow. You could load custom field data using a custom field added to product pages. And that is what we are going to do and that is also what most developers would do.

Creating Custom Fields

To create the custom field you could use Advanced Custom Fields (free or premium) or GenerateWP (Premium version for meta boxes) for example. Or you use the built in option and generate them on the product page. Does not look really pretty though. You can code them yourself too of course. For just one or two custom fields ACF would be overkill really but you could use it locally to generate the fields and then export them as PHP to add to your theme. AND you would need to include ACF in your theme.

Advanced Custom Fields Custom Field

To create a meta box or custom field in ACF you just to to advanced custom fields in the backend. Code I created in like a minute would be:

if( function_exists('acf_add_local_field_group') ):

acf_add_local_field_group(array (
 'key' => 'group_5881d7c5ba966',
 'title' => 'Product Page Meta Boxes',
 'fields' => array (
 array (
 'key' => 'field_5881d81b4ad68',
 'label' => 'Ingredients',
 'name' => 'ingredients',
 'type' => 'textarea',
 'instructions' => 'Fill in the ingredients for the product here',
 'required' => 0,
 'conditional_logic' => 0,
 'wrapper' => array (
 'width' => '',
 'class' => '',
 'id' => '',
 'default_value' => 'Text to be added here',
 'placeholder' => '',
 'maxlength' => '',
 'rows' => '6',
 'new_lines' => 'wpautop',
 'location' => array (
 array (
 array (
 'param' => 'post_type',
 'operator' => '==',
 'value' => 'product',
 'menu_order' => 0,
 'position' => 'normal',
 'style' => 'default',
 'label_placement' => 'top',
 'instruction_placement' => 'label',
 'hide_on_screen' => '',
 'active' => 1,
 'description' => '',


As stated this will not work without advanced custom fields included in your theme. So if you feel like this is overkill it would be better to add your own code.

Clean Generated Meta Box code

Coding yourself takes some time like I said and there is a site where you can generate a custom field for free. One that uses native code. It has been created by Jeremy Hixon and can be found here. I created one for WooCommerce products with a text area and the code looks like this:


 * Generated by the WordPress Meta Box generator
 * at

function ingredients_get_meta( $value ) {
 global $post;

$field = get_post_meta( $post->ID, $value, true );
 if ( ! empty( $field ) ) {
 return is_array( $field ) ? stripslashes_deep( $field ) : stripslashes( wp_kses_decode_entities( $field ) );
 } else {
 return false;

function ingredients_add_meta_box() {
 __( 'Ingredients', 'ingredients' ),
 __( 'Ingredients', 'ingredients' ),
add_action( 'add_meta_boxes', 'ingredients_add_meta_box' );

function ingredients_html( $post) {
 wp_nonce_field( '_ingredients_nonce', 'ingredients_nonce' ); ?>

<p>Product Ingredients</p>

 <label for="ingredients_product_ingredients_text"><?php _e( 'Product Ingredients Text', 'ingredients' ); ?></label><br>
 <textarea name="ingredients_product_ingredients_text" id="ingredients_product_ingredients_text" ><?php echo ingredients_get_meta( 'ingredients_product_ingredients_text' ); ?></textarea>

function ingredients_save( $post_id ) {
 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
 if ( ! isset( $_POST['ingredients_nonce'] ) || ! wp_verify_nonce( $_POST['ingredients_nonce'], '_ingredients_nonce' ) ) return;
 if ( ! current_user_can( 'edit_post', $post_id ) ) return;

if ( isset( $_POST['ingredients_product_ingredients_text'] ) )
 update_post_meta( $post_id, 'ingredients_product_ingredients_text', esc_attr( $_POST['ingredients_product_ingredients_text'] ) );
add_action( 'save_post', 'ingredients_save' );

 Usage: ingredients_get_meta( 'ingredients_product_ingredients_text' )

It does miss a couple of elements though like how many rows you would like to have and the text domain. And the meta key here is not as we have it inside the filter so that needs adjusting too. The new generator does have the text domain, but the amount of rows is not an automated option yet. Five rows are added by default. But that you could adjust that in the code yourself.

Basic Custom Field

Besides ACF or hand made meta boxes you could just use the built in version as long as aesthetics is not paramount. When you just want to add one in your product you need to make sure custom fields are active in screen options:

Custom Fields Active

and then you will see this block underneath your product text to add a new custom field:

Add a new Custom Field

Make sure the key (= name)  and value are the ones you use in your function to call them inside your theme. And that you of course save the custom field. Once that is done you can load it for all products.

Custom Callback Code

Here is the code to grab the custom field data:

global $woocommerce, $post;
if ( !empty($post->post_content) ) : ?>
 <?php $heading = apply_filters('woocommerce_product_description_heading', __('Product Description', 'woocommerce')); ?>
<h5><?php echo $heading; ?></h5>
<?php echo get_post_meta($post->ID, 'Ingredients', true); ?>
<?php endif; ?>

As you can see we use get_post_meta . That is the best way to grab a meta key or value. We added this code inside a separate ingredients.php file which we added under single-product/tabs and we load it with:

function ianua_product_ingredients_tab() {
 woocommerce_get_template( 'woocommerce/single-product/tabs/ingredients.php' );

but you could also add the code inside the filter itself as is shown above in the basic filter.


If your theme is styled for tabs properly already this should work and look well.  But if not you can always add specific classes andor wrap the code in a div to target that tab or all tabs more specifically. In my case so far it worked fine in several themes out of the box.

Final Custom Product Tab

So if all went well you were able to create a custom product tab for WooCommerce that is loaded under all products, either as a basic custom field, an ACF custom field or a custom generated custom field. And you added the necessary filter and function to load the tab as the next tab in line.


Tagged in : Tagged in : ,
Jasper Frumau

Jasper has been working with web frameworks and applications such as Laravel, Magento and his favorite CMS WordPress including Roots Trellis and Sage for more than a decade. He helps customers with web design and online marketing. Services provided are web design, ecommerce, SEO, content marketing. When Jasper is not coding, marketing a website, reading about the web or dreaming the internet of things he plays with his son, travels or run a few blocks.