DEV3 Technologies home

WooCommerce custom product tab with admin field

Add a custom tab on the single product page and a matching admin field, saved and escaped through WooCommerce hooks.

WooCommerceHookswoocommerceproduct-tabsmetahooks

Code

<?php
/**
 * Custom "Care guide" product tab.
 *
 * @package Acme_Core
 */

defined( 'ABSPATH' ) || exit;

// 1. Admin: a textarea in the General tab of the product data box.
add_action( 'woocommerce_product_options_general_product_data', static function (): void {
	woocommerce_wp_textarea_input( array(
		'id'          => '_acme_care_guide',
		'label'       => __( 'Care guide', 'acme-core' ),
		'description' => __( 'Shown in its own tab on the product page.', 'acme-core' ),
		'desc_tip'    => true,
	) );
} );

// 2. Save it. WooCommerce already verified the nonce and capability for this hook.
add_action( 'woocommerce_admin_process_product_object', static function ( WC_Product $product ): void {
	$value = isset( $_POST['_acme_care_guide'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
		? wp_kses_post( wp_unslash( $_POST['_acme_care_guide'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
		: '';
	$product->update_meta_data( '_acme_care_guide', $value );
} );

// 3. Front end: add the tab only when the product has content.
add_filter( 'woocommerce_product_tabs', static function ( array $tabs ): array {
	global $product;

	if ( $product instanceof WC_Product && '' !== trim( (string) $product->get_meta( '_acme_care_guide' ) ) ) {
		$tabs['acme_care'] = array(
			'title'    => __( 'Care guide', 'acme-core' ),
			'priority' => 25,
			'callback' => 'acme_care_tab_content',
		);
	}
	return $tabs;
} );

function acme_care_tab_content(): void {
	global $product;
	echo '<h2>' . esc_html__( 'Care guide', 'acme-core' ) . '</h2>';
	echo wp_kses_post( wpautop( (string) $product->get_meta( '_acme_care_guide' ) ) );
}

How it works

What it solves

Product pages often need extra information such as care, sizing or shipping notes that does not fit the description. A custom tab with its own admin field keeps that content structured, and the tab only appears when it has something to say.

How it works

Three hooks do the work. The first adds a textarea to the General panel using a WooCommerce helper. The second saves it through the product object on woocommerce_admin_process_product_object, which runs after WooCommerce has checked the nonce and permissions, and passes the text through wp_kses_post. The third filter adds a tab with a priority and a callback only when the stored value is non-empty.

The callback escapes the heading and filters the body again on output.

Adapting it

Change the key, label and priority; priority 10 is Description, 20 Additional information, 30 Reviews. Swap the textarea for a rich editor with wp_editor in a custom panel when authors need formatting. Use update_meta_data so the data works with high-performance order storage as well as classic storage.

Pitfalls

  • Use the product object, not update_post_meta, for forward compatibility.
  • Filtering on output as well as on save is deliberate defence in depth.
  • Variable products store tab data on the parent.
  • Tab priority collisions overwrite nothing but change the order.

How to use it

Keep the file in your plugin and load it after WooCommerce is active, for example by checking class_exists( 'WooCommerce' ).

Edit a product, fill in Care guide under General, and update.

View the product to see the new tab.

Licence: GPL-2.0-or-later. This snippet comes from WordPress code and is GPL-2.0-or-later.

Related snippets