WooCommerce Checkout Field Editor Code Generator
Add a custom checkout field with code for the Checkout block, the classic checkout, or both.
Your details
Paste into your child theme's functions.php or a code snippets plugin
/**
* Checkout field: How did you hear about us?
* Paste into your child theme's functions.php or a code snippets plugin.
* Test on a staging site first.
*/
// Checkout block: register the field with the Additional Checkout Fields API.
add_action( 'woocommerce_init', 'my_store_how_did_you_hear_register_block_field' );
function my_store_how_did_you_hear_register_block_field() {
if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) ) {
return;
}
woocommerce_register_additional_checkout_field(
array(
'id' => 'my-store/how-did-you-hear',
'label' => 'How did you hear about us?',
'location' => 'order',
'type' => 'select',
'required' => false,
'placeholder' => 'Select an option',
'options' => array(
array( 'value' => 'google', 'label' => 'Google' ),
array( 'value' => 'instagram', 'label' => 'Instagram' ),
array( 'value' => 'a-friend', 'label' => 'A friend' ),
array( 'value' => 'other', 'label' => 'Other' ),
),
)
);
}
// WooCommerce shows the value on the order screen, in emails and in the customer's account automatically.
// To read it in your own code:
// $checkout_fields = Automattic\WooCommerce\Blocks\Package::container()->get( Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields::class );
// $value = $checkout_fields->get_field_from_object( 'my-store/how-did-you-hear', $order, 'other' );
// Classic checkout: add the field with the woocommerce_checkout_fields filter.
add_filter( 'woocommerce_checkout_fields', 'my_store_how_did_you_hear_add_classic_field' );
function my_store_how_did_you_hear_add_classic_field( $fields ) {
$field = array(
'type' => 'select',
'label' => 'How did you hear about us?',
'required' => false,
'class' => array( 'form-row-wide' ),
'options' => array(
'' => 'Select an option',
'google' => 'Google',
'instagram' => 'Instagram',
'a-friend' => 'A friend',
'other' => 'Other',
),
);
$fields['order']['my_store_how_did_you_hear'] = $field;
return $fields;
}
// Save it to the order (HPOS-compatible: uses the order object, not post meta).
add_action( 'woocommerce_checkout_create_order', 'my_store_how_did_you_hear_save_classic_field', 10, 2 );
function my_store_how_did_you_hear_save_classic_field( $order, $data ) {
if ( isset( $data['my_store_how_did_you_hear'] ) && '' !== $data['my_store_how_did_you_hear'] ) {
$order->update_meta_data( '_my_store_how_did_you_hear', $data['my_store_how_did_you_hear'] );
}
}
function my_store_how_did_you_hear_label( $value ) {
$options = array(
'google' => 'Google',
'instagram' => 'Instagram',
'a-friend' => 'A friend',
'other' => 'Other',
);
return isset( $options[ $value ] ) ? $options[ $value ] : $value;
}
// Show it on the order edit screen.
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_store_how_did_you_hear_show_in_admin' );
function my_store_how_did_you_hear_show_in_admin( $order ) {
foreach ( array( '_my_store_how_did_you_hear' => 'How did you hear about us?' ) as $meta_key => $title ) {
$value = my_store_how_did_you_hear_label( $order->get_meta( $meta_key ) );
if ( '' !== (string) $value ) {
echo '<p><strong>' . esc_html( $title ) . ':</strong> ' . esc_html( $value ) . '</p>';
}
}
}
// Add it to order emails.
add_filter( 'woocommerce_email_order_meta_fields', 'my_store_how_did_you_hear_add_to_emails', 10, 3 );
function my_store_how_did_you_hear_add_to_emails( $fields, $sent_to_admin, $order ) {
foreach ( array( '_my_store_how_did_you_hear' => 'How did you hear about us?' ) as $meta_key => $title ) {
$value = my_store_how_did_you_hear_label( $order->get_meta( $meta_key ) );
if ( '' !== (string) $value ) {
$fields[ $meta_key ] = array(
'label' => $title,
'value' => esc_html( $value ),
);
}
}
return $fields;
}Each half only affects its own checkout, so it's safe to keep both. The two save to different meta keys, so if you switch checkouts later, older orders keep their values under the old key.
Block checkout values are saved under _wc_other/my-store/how-did-you-hear. Read them with the helper in the code rather than the raw meta key.
Don't combine this with a checkout field editor plugin. The WooCommerce docs say the plugin overrides custom checkout field code.
Two checkouts, two APIs
WooCommerce has two checkouts, and custom fields work differently in each. If you're not sure which one you use, open your checkout page in the editor: a Checkout block means the block checkout, a [woocommerce_checkout] shortcode means the classic one. Or pick Not sure: both.
| Checkout block | Classic checkout | |
|---|---|---|
| API | woocommerce_register_additional_checkout_field() on woocommerce_init | woocommerce_checkout_fields filter |
| Field types | text, select, checkbox | text, select, checkbox (and more) |
| Locations | contact, address, order | billing, shipping, order groups |
| Saving and display | Automatic: order screen, emails, My account | Billing and shipping fields save automatically. Order fields need the save hook the tool adds. |
The generated classic code saves with $order->update_meta_data(), so it works with High-Performance Order Storage (HPOS).
Installing the code
- 01
Use a child theme or a snippets plugin
Paste the code into your child theme's
functions.phpor a code snippets plugin. Don't edit the parent theme, or updates will wipe it. - 02
Remove any checkout field editor plugin
WooCommerce's docs say its Checkout Field Editor extension overrides custom checkout field code, so the two don't mix.
- 03
Place a test order
Fill in the field, place an order and check it shows on the order under WooCommerce → Orders and in the order email.
To hide Company, Phone or Address line 2 on the Checkout block, select the block in the editor and toggle those fields off. No code needed.
Things to know
- Address fields appear twice on the Checkout block (shipping and billing) and save two values.
- Contact fields are saved to the customer and show in their account. Order fields are saved to the order only.
- Don't rename the field ID once orders have values. The ID is part of the meta key.
- Date fields are listed in WooCommerce's docs but arrive with WooCommerce 11.2, which was still in beta at the time of writing. This tool sticks to types that work in 11.1.
Questions people ask
Can I add checkout fields in WooCommerce without a plugin?
Yes. A few lines of PHP register the field. This tool writes them for the Checkout block, the classic checkout or both.
Why doesn't woocommerce_checkout_fields work on my checkout?
That filter only affects the classic checkout. The Checkout block uses the Additional Checkout Fields API instead.
Where are the values stored?
Block fields use meta keys like _wc_other/my-store/field-id. Classic fields use _billing_…, _shipping_… or _my_store_… order meta.
Is this HPOS-compatible?
Yes. Everything reads and writes through the order object rather than post meta.
Can I make the field required?
Yes. Toggle Required. For a checkbox on the block checkout you can also set the error shown when it isn't ticked.
