How to Add Custom Product Tabs to Woocommerce Single Product Pages?

Looking for a way to present extra details on WooCommerce product pages beyond the standard tabs? This guide shows you how to add custom tabs without losing your work when you update your theme. I’ll show you how to place this code in WPCode—so you can safely manage snippets—and walk you through each step, from registering a Custom Post Type (CPT) to displaying tabs on product pages.

Why Add Custom Tabs to WooCommerce?

Sometimes, product details just don’t fit neatly under “Description” or “Reviews.” You might have special shipping info, sizing charts, or extended warranties. Custom tabs give you that extra room to inform and engage customers in a more structured way. See the screenshot:

How to Add Custom Product Tabs to Woocommerce Single Product Pages?

Option 1: Installation Using WPCode plugin (Recommended Method)

WPCode offers the safest and most user-friendly way to add this feature. Here’s how:

Install WPCode:

  • Go to Plugins → Add New
  • Search for “WPCode”
  • Install and activate the free plugin

Why WPCode instead of the theme’s functions.php? WPCode (or any code snippets plugin) keeps your custom tweaks separate from your theme. So if your theme updates, you won’t lose any changes.

Add the Code Snippet:

  • Navigate to Code Snippets → Add Snippet
  • Click “Add New Snippet”
  • Choose “PHP Snippets” as the code type
  • Name your snippet (e.g., “Gutenberg Add New Button”)
  • Please copy and paste our provided code
  • Set “Location” to “Run Everywhere”

Once you save and activate the snippet, you’re set to move on.

Option 2: Using the functions.php of Your Child Theme

⚠️ Warning: This method requires more technical knowledge and can break your site if not done carefully!

If you’re comfortable editing theme files, you can add the code directly to your child theme’s functions.php file.

Steps:

1. Access Your Child Theme’s functions.php:

  • Use FTP or the WordPress Theme Editor (Appearance > Theme Editor).

2. Add the Custom Code:. Paste the same code provided above at the end of the functions.php file.

3. Save the Changes.

Note: Editing the functions.php file can break your site if not done correctly. Always back up your site before making changes.

Next, I’ll show you the different parts of the code and explain what they do. If you want to see the full code, scroll down to the bottom and view it there.

Register the Custom Post Type (CPT)

The snippet below creates a CPT named Custom Tabs and places it in your WooCommerce menu. This allows you to add global tabs that apply to multiple products.

// Register 'Custom Tabs' Custom Post Type
function register_custom_tabs_cpt() {
    $args = array(
        'labels' => array(
            'name'          => __('Custom Tabs', 'your-textdomain'),
            'singular_name' => __('Custom Tab', 'your-textdomain'),
            'menu_name'     => __('Custom Tabs', 'your-textdomain'),
            'add_new'       => __('Add New', 'your-textdomain'),
            'add_new_item'  => __('Add New Custom Tab', 'your-textdomain'),
            'edit_item'     => __('Edit Custom Tab', 'your-textdomain'),
            'new_item'      => __('New Custom Tab', 'your-textdomain'),
        ),
        'public'        => false,
        'show_ui'       => true,
        'show_in_menu'  => 'edit.php?post_type=product',
        'capability_type' => 'post',
        'hierarchical'  => false,
        'supports'      => array('title', 'editor'),
        'menu_icon'     => 'dashicons-welcome-widgets-menus',
        'show_in_rest'  => true, // Enables Gutenberg support
    );

    register_post_type('custom_product_tab', $args);
}
add_action('init', 'register_custom_tabs_cpt');

How It Integrates

show_in_menu attaches our CPT to the WooCommerce products menu.

show_in_rest enables the Gutenberg editor for these custom tabs.

4. Add Meta Boxes for Product Categories

Want certain tabs to appear only for specific product categories? Here’s how:

// Add Product Category Meta Box to Custom Tabs
function add_custom_tab_meta_box() {
    add_meta_box(
        'custom_tab_product_categories',
        __('Assign to Product Categories', 'your-textdomain'),
        'render_custom_tab_meta_box',
        'custom_product_tab',
        'side',
        'default'
    );
}
add_action('add_meta_boxes', 'add_custom_tab_meta_box');

// Render the Meta Box
function render_custom_tab_meta_box($post) {
    $selected_categories = get_post_meta($post->ID, '_custom_tab_product_categories', true);
    $selected_categories = is_array($selected_categories) ? $selected_categories : array();

    $product_categories = get_terms(array(
        'taxonomy'   => 'product_cat',
        'hide_empty' => false,
    ));

    echo '<p>Select product categories for this tab:</p>';
    echo '<ul>';
    foreach ($product_categories as $category) {
        $checked = in_array($category->term_id, $selected_categories) ? 'checked' : '';
        echo '<li><label><input type="checkbox" name="custom_tab_product_categories[]" value="' . esc_attr($category->term_id) . '" ' . $checked . '> ' . esc_html($category->name) . '</label></li>';
    }
    echo '</ul>';
}

// Save Selected Product Categories
function save_custom_tab_meta($post_id) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

    if (isset($_POST['custom_tab_product_categories'])) {
        $selected_categories = array_map('sanitize_text_field', $_POST['custom_tab_product_categories']);
        update_post_meta($post_id, '_custom_tab_product_categories', $selected_categories);
    } else {
        delete_post_meta($post_id, '_custom_tab_product_categories');
    }
}
add_action('save_post', 'save_custom_tab_meta');

Practical Example

Imagine you have “Hoodies” and “T-Shirts” as product categories. You can create a “Fabric Care” custom tab and assign it only to “Hoodies.” This ensures the tab only shows where needed.

Display the Tabs on Product Pages

This code fetches global custom tabs (from the CPT) that match the product’s categories and merges them with any product-specific tabs.

// Display Global and Product-Specific Tabs on Product Pages
function display_global_custom_tabs($tabs) {
    global $post;

    if (!$post) return $tabs;

    // Get the product categories
    $product_categories = wp_get_post_terms($post->ID, 'product_cat', array('fields' => 'ids'));

    // Fetch all published global tabs
    $global_tabs = get_posts(array(
        'post_type'      => 'custom_product_tab',
        'posts_per_page' => -1,
        'post_status'    => 'publish',
    ));

    // Merge global tabs if they match product categories
    foreach ($global_tabs as $tab) {
        $assigned_categories = get_post_meta($tab->ID, '_custom_tab_product_categories', true);
        if (is_array($assigned_categories) && array_intersect($assigned_categories, $product_categories)) {
            $tabs['global_tab_' . $tab->ID] = array(
                'title'    => get_the_title($tab->ID),
                'priority' => 40,
                'callback' => 'render_global_custom_tab_content',
                'content'  => apply_filters('the_content', get_post_field('post_content', $tab->ID)),
            );
        }
    }

    // Add a single product's custom tab
    $custom_tab_title   = get_post_meta($post->ID, '_custom_tab_title', true);
    $custom_tab_content = get_post_meta($post->ID, '_custom_tab_content', true);

    if (!empty($custom_tab_title) && !empty($custom_tab_content)) {
        $tabs['custom_tab'] = array(
            'title'    => esc_html($custom_tab_title),
            'priority' => 50,
            'callback' => 'render_custom_tab_content',
            'content'  => apply_filters('the_content', $custom_tab_content),
        );
    }

    return $tabs;
}
add_filter('woocommerce_product_tabs', 'display_global_custom_tabs');

// Render Global Custom Tab Content
function render_global_custom_tab_content($key, $tab) {
    echo '<div class="woocommerce-global-tab-content">';
    echo do_shortcode(wpautop($tab['content']));
    echo '</div>';
}

// Render Single Product Custom Tab Content
function render_custom_tab_content($key, $tab) {
    echo '<div class="woocommerce-custom-tab-content">';
    echo do_shortcode(wpautop($tab['content']));
    echo '</div>';
}

Product-Specific Tabs

You can store extra details on a per-product basis. Your customer sees a special tab only on that product’s page.

Adding a Custom Tab in the Product Edit Screen

These functions create a “Custom Tab” panel in your product’s edit screen, letting you fill in a title and content that appear as a separate tab.

// Register Custom Tab in Product Edit Page
function add_custom_product_tab($tabs) {
    $tabs['custom_tab'] = array(
        'label'    => __('Custom Tab', 'your-textdomain'),
        'target'   => 'custom_product_data',
        'class'    => array('show_if_simple', 'show_if_variable'),
        'priority' => 50,
    );
    return $tabs;
}
add_filter('woocommerce_product_data_tabs', 'add_custom_product_tab');

// Add Custom Tab Fields
function custom_product_tab_content() {
    global $post;
    $custom_tab_title = get_post_meta($post->ID, '_custom_tab_title', true);
    $custom_tab_content = get_post_meta($post->ID, '_custom_tab_content', true);
    ?>
    <div id="custom_product_data" class="panel woocommerce_options_panel">
        <div class="options_group">
            <?php
            woocommerce_wp_text_input(array(
                'id'    => '_custom_tab_title',
                'label' => __('Custom Tab Title', 'your-textdomain'),
                'value' => esc_attr($custom_tab_title),
            ));
            ?>
            <p class="form-field">
                <label for="_custom_tab_content"><?php _e('Custom Tab Content', 'your-textdomain'); ?></label>
                <?php
                wp_editor(
                    $custom_tab_content,
                    '_custom_tab_content',
                    array(
                        'textarea_name' => '_custom_tab_content',
                        'media_buttons' => true,
                        'textarea_rows' => 8,
                    )
                );
                ?>
            </p>
        </div>
    </div>
    <?php
}
add_action('woocommerce_product_data_panels', 'custom_product_tab_content');

// Save Custom Tab Data
function save_custom_product_tab_data($post_id) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

    if (isset($_POST['_custom_tab_title'])) {
        update_post_meta($post_id, '_custom_tab_title', sanitize_text_field($_POST['_custom_tab_title']));
    }
    if (isset($_POST['_custom_tab_content'])) {
        update_post_meta($post_id, '_custom_tab_content', wp_kses_post($_POST['_custom_tab_content']));
    }
}
add_action('woocommerce_process_product_meta', 'save_custom_product_tab_data');

Complete Code for Adding Custom Product Tabs to WooCommerce Single Product Pages

Here’s the complete code with all the parts mentioned above included.

// Add Custom Product Tabs to Woocommerce Single Product Pages
// Exit if accessed directly
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Register 'Custom Tabs' Custom Post Type with Gutenberg Support
 */
function register_custom_tabs_cpt() {
    $args = array(
        'labels' => array(
            'name'               => __('Custom Tabs', 'your-textdomain'),
            'singular_name'      => __('Custom Tab', 'your-textdomain'),
            'menu_name'          => __('Custom Tabs', 'your-textdomain'),
            'add_new'            => __('Add New', 'your-textdomain'),
            'add_new_item'       => __('Add New Custom Tab', 'your-textdomain'),
            'edit_item'          => __('Edit Custom Tab', 'your-textdomain'),
            'new_item'           => __('New Custom Tab', 'your-textdomain'),
        ),
        'public'        => false,
        'show_ui'       => true,
        'show_in_menu'  => 'edit.php?post_type=product',
        'capability_type' => 'post',
        'hierarchical'  => false,
        'supports'      => array('title', 'editor'),
        'menu_icon'     => 'dashicons-welcome-widgets-menus',
        'show_in_rest'  => true, // ✅ Enables Gutenberg support
    );

    register_post_type('custom_product_tab', $args);
}
add_action('init', 'register_custom_tabs_cpt');

/**
 * Add Product Category Meta Box to Custom Tabs
 */
function add_custom_tab_meta_box() {
    add_meta_box(
        'custom_tab_product_categories',
        __('Assign to Product Categories', 'your-textdomain'),
        'render_custom_tab_meta_box',
        'custom_product_tab',
        'side',
        'default'
    );
}
add_action('add_meta_boxes', 'add_custom_tab_meta_box');

/**
 * Render Product Category Meta Box
 */
function render_custom_tab_meta_box($post) {
    $selected_categories = get_post_meta($post->ID, '_custom_tab_product_categories', true);
    $selected_categories = is_array($selected_categories) ? $selected_categories : array();

    $product_categories = get_terms(array(
        'taxonomy'   => 'product_cat',
        'hide_empty' => false,
    ));

    echo '<p>' . __('Select product categories for this tab:', 'your-textdomain') . '</p>';
    echo '<ul>';
    foreach ($product_categories as $category) {
        $checked = in_array($category->term_id, $selected_categories) ? 'checked' : '';
        echo '<li><label><input type="checkbox" name="custom_tab_product_categories[]" value="' . esc_attr($category->term_id) . '" ' . $checked . '> ' . esc_html($category->name) . '</label></li>';
    }
    echo '</ul>';
}

/**
 * Save Selected Product Categories for Custom Tabs
 */
function save_custom_tab_meta($post_id) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

    if (isset($_POST['custom_tab_product_categories'])) {
        $selected_categories = array_map('sanitize_text_field', $_POST['custom_tab_product_categories']);
        update_post_meta($post_id, '_custom_tab_product_categories', $selected_categories);
    } else {
        delete_post_meta($post_id, '_custom_tab_product_categories');
    }
}
add_action('save_post', 'save_custom_tab_meta');

/**
 * Display Global Custom Tabs on Product Pages with Proper Styling
 */
function display_global_custom_tabs($tabs) {
    global $post;

    if (!$post) return $tabs;

    $product_categories = wp_get_post_terms($post->ID, 'product_cat', array('fields' => 'ids'));

    $args = array(
        'post_type'      => 'custom_product_tab',
        'posts_per_page' => -1,
        'post_status'    => 'publish',
    );

    $global_tabs = get_posts($args);

    foreach ($global_tabs as $tab) {
        $assigned_categories = get_post_meta($tab->ID, '_custom_tab_product_categories', true);
        if (is_array($assigned_categories) && array_intersect($assigned_categories, $product_categories)) {
            $tabs['global_tab_' . $tab->ID] = array(
                'title'    => esc_html(get_the_title($tab->ID)),
                'priority' => 40,
                'callback' => 'render_global_custom_tab_content',
                'content'  => apply_filters('the_content', get_post_field('post_content', $tab->ID)),
            );
        }
    }

    // Add custom tab to the product tabs array
    $custom_tab_title = get_post_meta($post->ID, '_custom_tab_title', true);
    $custom_tab_content = get_post_meta($post->ID, '_custom_tab_content', true);

    if (!empty($custom_tab_title) && !empty($custom_tab_content)) {
        $tabs['custom_tab'] = array(
            'title'    => esc_html($custom_tab_title),
            'priority' => 50,
            'callback' => 'render_custom_tab_content',
            'content'  => apply_filters('the_content', $custom_tab_content),
        );
    }

    return $tabs;
}
add_filter('woocommerce_product_tabs', 'display_global_custom_tabs');

/**
 * Render Global Custom Tab Content with Proper Styles
 */
function render_global_custom_tab_content($key, $tab) {
    echo '<div class="woocommerce-global-tab-content">';
    echo do_shortcode(wpautop($tab['content']));
    echo '</div>';
}

/**
 * Render Custom Tab Content
 */
function render_custom_tab_content($key, $tab) {
    echo '<div class="woocommerce-custom-tab-content">';
    echo do_shortcode(wpautop($tab['content']));
    echo '</div>';
}

/**
 * Enqueue Gutenberg Frontend Styles for Global Tabs
 */
function enqueue_gutenberg_styles_for_tabs() {
    if (is_product()) {
        wp_enqueue_style('wp-block-library'); // Core Gutenberg styles
    }
}
add_action('wp_enqueue_scripts', 'enqueue_gutenberg_styles_for_tabs');

/**
 * Register Custom Tab in Product Edit Page
 */
function add_custom_product_tab($tabs) {
    $tabs['custom_tab'] = array(
        'label'    => __('Custom Tab', 'your-textdomain'),
        'target'   => 'custom_product_data',
        'class'    => array('show_if_simple', 'show_if_variable'),
        'priority' => 50,
    );
    return $tabs;
}
add_filter('woocommerce_product_data_tabs', 'add_custom_product_tab');

/**
 * Add Custom Tab Fields to Product Edit Page
 */
function custom_product_tab_content() {
    global $post;
    $custom_tab_title = get_post_meta($post->ID, '_custom_tab_title', true);
    $custom_tab_content = get_post_meta($post->ID, '_custom_tab_content', true);
    ?>
    <div id="custom_product_data" class="panel woocommerce_options_panel">
        <div class="options_group">
            <?php
            woocommerce_wp_text_input(array(
                'id'          => '_custom_tab_title',
                'label'       => __('Custom Tab Title', 'your-textdomain'),
                'value'       => esc_attr($custom_tab_title),
            ));
            ?>
            <p class="form-field">
                <label for="_custom_tab_content"><?php _e('Custom Tab Content', 'your-textdomain'); ?></label>
                <?php
                wp_editor(
                    $custom_tab_content,
                    '_custom_tab_content',
                    array(
                        'textarea_name' => '_custom_tab_content',
                        'media_buttons' => true,
                        'textarea_rows' => 8,
                    )
                );
                ?>
            </p>
        </div>
    </div>
    <?php
}
add_action('woocommerce_product_data_panels', 'custom_product_tab_content');

/**
 * Save Custom Tab Data
 */
function save_custom_product_tab_data($post_id) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

    if (isset($_POST['_custom_tab_title'])) {
        update_post_meta($post_id, '_custom_tab_title', sanitize_text_field($_POST['_custom_tab_title']));
    }
    if (isset($_POST['_custom_tab_content'])) {
        update_post_meta($post_id, '_custom_tab_content', wp_kses_post($_POST['_custom_tab_content']));
    }
}
add_action('woocommerce_process_product_meta', 'save_custom_product_tab_data');

Troubleshooting Common Issues

1. Tabs Not Appearing

  • Make sure you have published your custom tabs (not left them as drafts).
  • Double-check that you’re assigning the correct categories.

2. Meta Box Values Not Saving

  • Confirm that DOING_AUTOSAVE isn’t interfering.
  • Check user permissions (you need the correct capability to edit/post).

Do you want to thank me and buy me a beer?

Every donation is entirely welcome but NEVER required. Enjoy my work for free but if you would like to thank me and buy me a beer or two then you can use this form here below.

Donation Form (#2)

Here are some of my favorite WordPress tools

Thanks for reading this article! I hope it's been useful as you work on your own websites and e-commerce sites. I wanted to share some tools I use as a WordPress developer, and I think you'll find them helpful too.

Just so you know, these are affiliate links. If you decide to use any of them, I'll earn a commission. This helps me create tutorials and YouTube videos. But honestly, I genuinely use and recommend these tools to my friends and family as well. Your support keeps me creating content that benefits everyone.

Themes: Over the past few years, I've consistently relied on two primary themes for all sorts of projects: the Blocksy theme and the Kadence Theme. If you explore this website and my YouTube channel, you'll come across numerous tutorials that delve into these themes. If you're interested in obtaining a 10% discount for both of these themes, then:

Code Snippets Manager: WPCodeBox allows you to add code snippets to your site. Not only that, but it also provides you with the capability to construct and oversee your WordPress Code Snippets library right in the cloud. You can grab it with the 20% discount here (SAVE 20% Coupon: WPSH20).

Contact forms: There are hundreds of contact forms out there but Fluent Forms is the one I like the most. If you need a 20% discount then use this link (save 20% coupon is WPSH20).

Gutenberg add-ons: If I need a good Gutenberg blocks add-on then Kadence Blocks is the one I have used the most. You’ll get a 10% discount with the coupon SIMPLEHACKS here.

Website migration: While building a website you probably need a good plugin that can help you with the migration, backups, restoration, and staging sites. Well, WpVivid is the one I have used for the last couple of years. If you use this link along with the WPSH20 coupon you’ll get a 20% discount.

Woocommerce extensions: There are a bunch of Woocommerce extensions that I like but the one that stands out is Advanced Dynamic Pricing. Once again, you’ll get a 20% discount if you use this link here (save 20% coupon is WPSH20)

Web Hosting: If you would like to have a really fast and easy-to-use managed cloud hosting, then I recommend Verpex Hosting (see my review here). By the way, this site is hosted in Verpex.)

To see all my most up-to-date recommendations, check out this resource that I made for you!

Janek T.
Janek T.

Improve this text: {CLIPBOARD}

- I have been passionate about Wordpress since 2011, creating websites and sharing valuable tips on using Wordpress and Woocommerce on my site.
- Be the first to receive notifications about new tutorials by subscribing to my Youtube channel .
- Follow me on Twitter here

Articles: 137