WooCommerce: Product Add-Ons (Without a Plugin!)

WooCommerce product add-ons are custom input fields that show on the single product page. They’re called “add-ons” as you can add a product personalization or an upsell (at a cost of course).

For example, you can display a text input to print something on the product. Or radio buttons to select different kinds of product upgrades. Or a checkbox to upsell gift wrapping.

Either way, and of course, there are plugins for that. But first, I want to give you a tutorial to code this by yourself (case study: global custom input text field and no surcharge), so that you can learn something new. Enjoy!

Add a custom input field to each product @ WooCommerce Single Product page

PHP snippet: Show Custom Input Field @ WooCommerce Single Product Page

/**
 * @snippet       Add input field to products - WooCommerce
 * @how-to        Watch tutorial @ https://businessbloomer.com/?p=19055
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 3.5.7
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */

// -----------------------------------------
// 1. Show custom input field above Add to Cart

add_action( 'woocommerce_before_add_to_cart_button', 'bbloomer_product_add_on', 9 );

function bbloomer_product_add_on() {
    $value = isset( $_POST['_custom_text_add_on'] ) ? sanitize_text_field( $_POST['_custom_text_add_on'] ) : '';
    echo '<div><label>Custom Text Add-On <abbr class="required" title="required">*</abbr></label><p><input name="_custom_text_add_on" value="' . $value . '"></p></div>';
}

// -----------------------------------------
// 2. Throw error if custom input field empty

add_filter( 'woocommerce_add_to_cart_validation', 'bbloomer_product_add_on_validation', 10, 3 );

function bbloomer_product_add_on_validation( $passed, $product_id, $qty ){
	if( isset( $_POST['_custom_text_add_on'] ) && sanitize_text_field( $_POST['_custom_text_add_on'] ) == '' ) {
		wc_add_notice( 'Custom Text Add-On is a required field', 'error' );
		$passed = false;
	}
    return $passed;
}

// -----------------------------------------
// 3. Save custom input field value into cart item data

add_filter( 'woocommerce_add_cart_item_data', 'bbloomer_product_add_on_cart_item_data', 10, 2 );

function bbloomer_product_add_on_cart_item_data( $cart_item, $product_id ){
    if( isset( $_POST['_custom_text_add_on'] ) ) {
        $cart_item['custom_text_add_on'] = sanitize_text_field( $_POST['_custom_text_add_on'] );
    }
    return $cart_item;
}

// -----------------------------------------
// 4. Display custom input field value @ Cart

add_filter( 'woocommerce_get_item_data', 'bbloomer_product_add_on_display_cart', 10, 2 );

function bbloomer_product_add_on_display_cart( $_data, $cart_item ) {
    if ( isset( $cart_item['custom_text_add_on'] ) ){
        $data[] = array(
            'name' => 'Custom Text Add-On',
            'value' => sanitize_text_field( $cart_item['custom_text_add_on'] )
        );
    }
    return $data;
}

// -----------------------------------------
// 5. Save custom input field value into order item meta

add_action( 'woocommerce_add_order_item_meta', 'bbloomer_product_add_on_order_item_meta', 10, 2 );

function bbloomer_product_add_on_order_item_meta( $item_id, $values ) {
    if ( ! empty( $values['custom_text_add_on'] ) ) {
        wc_add_order_item_meta( $item_id, 'Custom Text Add-On', $values['custom_text_add_on'], true );
    }
}

// -----------------------------------------
// 6. Display custom input field value into order table

add_filter( 'woocommerce_order_item_product', 'bbloomer_product_add_on_display_order', 10, 2 );

function bbloomer_product_add_on_display_order( $cart_item, $order_item ){
    if( isset( $order_item['custom_text_add_on'] ) ){
        $cart_item_meta['custom_text_add_on'] = $order_item['custom_text_add_on'];
    }
    return $cart_item;
}

// -----------------------------------------
// 7. Display custom input field value into order emails

add_filter( 'woocommerce_email_order_meta_fields', 'bbloomer_product_add_on_display_emails' );

function bbloomer_product_add_on_display_emails( $fields ) { 
    $fields['custom_text_add_on'] = 'Custom Text Add-On';
    return $fields; 
}

Where to add this code?

You can place PHP snippets at the bottom of your child theme functions.php file (before "?>" if you have it). CSS, on the other hand, goes in your child theme style.css file. Make sure you know what you are doing when editing such delicate files - if you need more guidance, please take a look at my free WooCommerce Customization video tutorial.

Does this snippet still work?

Please let me know in the comments if everything worked as expected. I would be happy to revise the snippet if you report otherwise (please provide screenshots). I have tested this code with Storefront theme, the WooCommerce version listed above and a WordPress-friendly hosting on PHP 7.

If you think this code saved you time & money, please join other Business Bloomer supporters and avail of 365 days of WooCommerce benefits. Thank you in advance :)

Need Help with WooCommerce?

Check out these free videos, tutorials and tips!

  • how-to-edit-woocommerce-with-php-snippets
  • woocommerce-hooks-add_action-list-visual
  • woocommerce-customize-single-product-page-PHP
  • woocommerce-customize-shop-page-PHP
  • woocommerce-advanced-customization
  • how-to-edit-woocommerce-cart
  • woocommerce-customize-checkout-page-PHP
  • woocommerce-email-customization
  • woocommerce-conditional-logic

Rodolfo Melogli

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as a WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza.

12 thoughts on “WooCommerce: Product Add-Ons (Without a Plugin!)

  1. Great code Sir!

    Thank You very much.

    I have 1 question regarding the product input field: is it possible to check (via Ajax?) if desired entry for a specific product already exist and if so notify customer to input some other value?

    Thanks in advance

    1. Hello Simo, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  2. Great post, thank you very much for the time and effort.
    Could you give us a hint on how to add a surcharge depending on product add-ons such as the text field of this snippet?
    Iยดm guessing there must be some filter hooks that allow it, but I am having difficulties finding them among all the info of the WooCommerce documentation.

  3. Your page is a greate help to understand how woocommerce / php work and to modify.
    Just have the problem that I have 2 languages. Normal pages and products are translated with WPML but if I do something in function.php how can I check the language or give different content for each language – for example the field description?

  4. Hi, this is great!
    However, I’ve tested it on a couple of sites and doesn’t seem to save/show the field’s value on the cart, checkout or confirmation email. Is this code saving the value in the database?
    Thank you!!

  5. it doesn’t carry the text over to the cart and neither does it save it to the order or display the text on the thank you page.

    1. You’re right Jess, sorry, I got confused between “_custom_text_add_on” and “custom_text_add_on” in part 3. Should work now, please let me know ๐Ÿ™‚

  6. I think this is a great addition to products such as nameplate necklaces. I’ve added a custom field to the checkout page for such instances. BUT… how would you implement this code to show up only on certain product pages and not all of them?

    1. Hello Alexander, I suggest you take a look at “conditional logic”: https://businessbloomer.com/conditional-logic-woocommerce-tutorial/ and https://businessbloomer.com/woocommerce-conditional-logic-ultimate-php-guide/. Let me know ๐Ÿ™‚

Questions? Feedback? Support? Leave your Comment Now!
_____

If you are writing code, please wrap it between: [php]code_here[/php]. Failure to complying with this (as well as going off topic) will result in comment deletion. You should expect a reply in about a week - this is a popular blog but I need to get paid work done first. Please consider joining #BloomerArmada to ask me 1-to-1 WooCommerce questions. Thank you :)

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.