How can I add a custom field to a Woocommerce product, AND have that field appear in the emails generated from the purchase?

There is very little documentation about this, and half of that seems to be from years ago when the code structure was different.

1 Answer 1


After much fiddling, we have figured it out. Here is the documentation we wrote for ourselves. It is very disappointing that Woocommerce could not be bothered to write their own documentation for this (instead, only link to an expensive plugin).

I have not added the parts which talk about customising the template for the product order, I didn't do that bit.

Background: PHP runs a heap of code to generate a page. There are lots of filters run before to "process" the data and set things up, and then 1 action is executed at the end to basically print out the HTML of the resulting page.

A "filter" is run before pages start rendering. An "action" is run TO write the page, ie to write out the generated HTML to send to the webbrowser client.

You can add in your own filters and actions at certain points that Woocommerce provides (eg to print out a button before the checkout button). There are specified points to "hook" into, where wordpress/woocommerce will run your custom functions.

To add your own custom things, you edit the file: public_html/wp-content/themes/Divi-child/functions.php and call either add_filter() or add_action(), with: * a key name of the woocommerce hook * the text name of your function you want to run during that hook point. Your function is added to a list that Woocommerce will run at a particular point in time during execution.

So, steps for a custom field. Example is "eye colour", the display title will be "Eye Colour" and the key is "eye_colour".

Step 1: Add an "action" to the checkout page, to render this new field.

//            (run this after 'order notes')   (call it 

custom_WHATEVERKEY_checkout_field - same as function name)
//                         |                             |
//                         |                             |
//                         v                             v
add_action( 'woocommerce_after_order_notes', 'custom_eye_colour_checkout_field' );

//       function name must match text above in add_action
function custom_eye_colour_checkout_field( $checkout ) {

    // div id can be anything, you use this id to match CSS styling
    // 'echo' will 'print' out HTML to the resulting page... this is the point of 'actions'
    echo '<div id="custom_eye_colour_checkout_field"><h2>' . __('Eye Colour') . '</h2>';

    // 'eye_colour' is the key... 'class' will be the CSS styling class
    // Label is displayed in the order page,
    // Placeholder is what is written in the little text box before user starts typing.
    woocommerce_form_field( 'eye_colour', array(
        'type'          => 'text', // could be a number or something else, page will help validate
        'class'         => array('my-field-class form-row-wide'), // CSS style class
        'label'         => __('Eye Colour (whatever you like in the title)'),
        'placeholder'   => __('Enter the eye colour you would like, display in the entry text box'),
        ), $checkout->get_value( 'eye_colour' ));
    // note the eye_colour key at the bottom line here

    // and finish the little HTML enclosing div.
    echo '</div>';

Step 2: Suck in the typed in 'eye colour' from the user, and store it with the order details.

The user will hit "Place Order", which is an ordinary HTML form, and that will submit all the typed in details via HTTP POST fields as per usual in the web world.

So we have to bring that field in and 'attach' it to the order with a key. This is done via an 'action', not sure exactly of the mechanism or when this is executed.

Note that below, the text-key ** here,

update_post_meta( $order_id, ' ** eye_colour ** ', sanitize_text_field( $_POST['eye_colour'] ) );

Does not need to be the same as the POST key, its a key attached to the order. But, lets keep it the same going onwards. Note that we usually use this_kind_of_naming for keys just as a convention.

add_action( 'woocommerce_checkout_update_order_meta', 'custom_eye_colour_checkout_field_update_order_meta' );

function custom_eye_colour_checkout_field_update_order_meta( $order_id ) {
    if ( ! empty( $_POST['eye_colour'] ) ) {
        update_post_meta( $order_id, 'eye_colour', sanitize_text_field( $_POST['eye_colour'] ) );

Step 3 -- show this field in the 'order edit' page, which I assume is in the backend of Wordpress.

add_action( 'woocommerce_admin_order_data_after_billing_address', 'custom_eye_colour_checkout_field_display_admin_order_meta', 10, 1 );

function custom_eye_colour_checkout_field_display_admin_order_meta($order){
    echo '<p><strong>'.__('Eye Colour').':</strong> ' . get_post_meta( $order->id, 'eye_colour', true ) . '</p>';

Step 4 -- show this in the email!

The magic raindance shortcode is

[ec_custom_field key="eye_colour"]

Put that in the "Main Text" in the Woocommerce "Email Customizer", and it will replace that chunk with the text that the user typed into the order form.

Step 5 -- show this in order-edit page

(Display in the backend where you look at all the orders that people have put through)

Again, we will add a hook into the functions.php Note that the hook name is called "woocommerce_admin_order_data_after_billing_address", So this information will appear AFTER the 'billing address' section. There are likely a bunch of other hooks so you can display this information at a different point in the page.

add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );

function my_custom_checkout_field_display_admin_order_meta($order){
    echo '<p><strong>'.__('Eye Colour').':</strong> ' . get_post_meta( $order->id, 'eye_colour', true ) . '</p>';

Step 6 -- Bonus level -- what fields are available to ec_custom_field?

It is not obvious what code is required in the [ec_custom_field key="???"] part. So lets display a list of these fields in the product-order page, in the backend of Wordpress (ie the Admin section where you can see all the product orders that people have put through).

Again, we will add a hook into the functions.php This time, we will print out a list of all the fields attached to the order (its attached to the HTTP post, it seems).

add_action( 'woocommerce_admin_order_data_after_billing_address', 'debug_checkout_field_display_admin_order_meta', 10, 1 );

function debug_checkout_field_display_admin_order_meta($order){
    echo '<p><strong>'.__('(debug) List fields').':</strong>';
    $vals = get_post_meta( $order->id );
    // Assume each value is a 1 element array, seems to be the situation.
    foreach($vals as $key => $val) {
        echo '<br/>* <strong>' . $key . ':</strong> ' . $val[0];
    /* To print out all the elements in the value-keys...
    foreach($vals as $key => $val) {
        echo '<br/>* <strong>' . $key . ':</strong> ' . $val;
        foreach($val as $key2 => $val2) {
            echo '<br/>** <strong>' . $key2 . ':</strong> ' . $val2;
    echo '<br/>Thats it.<br/>';
    echo '</p>';

That will print out a list of all the keys and their values, so you don't have to guess what key you need to use.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.