Custom product fields in the admin panel
The standard product properties are not always enough: you may need a lead time, a supplier, a link to a manual. WooCommerce lets you add a field right into the product editor — a manager fills it in the admin panel and the code reads it from meta data.
Where to write the code
Create the includes/admin.php file and include it in acme-shop.php. Everything that changes the admin panel goes there.
A field in the editor
// wp-content/plugins/acme-shop/includes/admin.php
add_action('woocommerce_product_options_general_product_data', function () {
woocommerce_wp_text_input([
'id' => '_acme_lead_time',
'label' => 'Lead time, days',
'type' => 'number',
'custom_attributes' => ['min' => 0, 'step' => 1],
]);
});Open any product in the admin panel: a number field has appeared in the Product data box on the General tab, under the prices. woocommerce_wp_text_input() draws the field itself and fills in the saved value from the meta data whose key equals id.
Saving
The field itself saves nothing. The value is written in a hook that fires when the product is saved:
// wp-content/plugins/acme-shop/includes/admin.php
add_action('woocommerce_admin_process_product_object', function (WC_Product $product) {
$days = isset($_POST['_acme_lead_time']) ? absint($_POST['_acme_lead_time']) : 0;
$product->update_meta_data('_acme_lead_time', $days);
});Enter 3 in the field and press Update — the value stays in the field after the reload. absint() turns the input into a non-negative integer. There is no need to call save(): WooCommerce saves the product itself right after the hook, and it has already checked the user's permissions.
Showing it in the storefront
The value is read where the customer needs it:
// wp-content/plugins/acme-shop/includes/storefront.php
add_action('woocommerce_single_product_summary', function () {
global $product;
$days = (int) $product->get_meta('_acme_lead_time');
if ($days > 0) {
printf('<p class="acme-lead-time">Made in %d days</p>', $days);
}
}, 25);On the product page the line Made in 3 days appears between the short description and the button.
Other fields and tabs
| Function | Field |
|---|---|
woocommerce_wp_text_input() | A string or a number |
woocommerce_wp_textarea_input() | Multi-line text |
woocommerce_wp_select() | A dropdown, values in options |
woocommerce_wp_checkbox() | A checkbox, stores yes or no |
| Hook | Tab of the Product data box |
|---|---|
woocommerce_product_options_general_product_data | General |
woocommerce_product_options_inventory_product_data | Inventory |
woocommerce_product_options_shipping | Shipping |
woocommerce_product_options_advanced | Advanced |
A column in the product list
// wp-content/plugins/acme-shop/includes/admin.php
add_filter('manage_edit-product_columns', function (array $columns): array {
$columns['acme_lead_time'] = 'Lead time';
return $columns;
});
add_action('manage_product_posts_custom_column', function (string $column, int $postId) {
if ($column === 'acme_lead_time') {
$product = wc_get_product($postId);
echo esc_html($product ? $product->get_meta('_acme_lead_time') : '');
}
}, 10, 2);The filter adds the column heading, the action fills the cells. A Lead time column with the number of days appears in the Products list.
Common mistake
The field was added but the save hook was not: the value disappears after pressing Update. A field and its saving are always two separate functions with the same key. The second cause of a vanished field is the new block-based product editor being enabled (WooCommerce → Settings → Advanced → Features): the woocommerce_product_options_... hooks do not work in it.