Customizing checkout fields using actions and filters

If you are not familiar with actions and filters or do not know how to implement this code we have an extension that can help with this which you can find here: WooCommerce Checkout Field Editor – If you install and activate this extension any code you implement as shown below would not work. You can not have custom checkout field code in your functions.php file when the extension is activated.
Please note: We are not able to provide support for customizations, if you are not able to get the following to work, please purchase the extension or contact a recommended Woo Worker.
Custom code should be copied into your child theme’s functions.php file.

How are checkout fields loaded into WooCommerce? ↑ Back to Top

The fields for the checkout (billing and shipping) come from the countries class (class-wc-countries.php) and the get_address_fields function- this is so locale settings are applied to the fields letting WooCommerce enable/disable fields based on the users location.

Before returning the fields, WC will run the fields through a filter. This allows them to be edited by third party plugins and themes (and your own custom code).


$address_fields = apply_filters('woocommerce_billing_fields', $address_fields);


$address_fields = apply_filters('woocommerce_shipping_fields', $address_fields);

The checkout class adds the loaded fields to its ‘checkout_fields’ array, as well as adding a few other fields like “order notes”.

$this->checkout_fields['billing']    = $woocommerce->countries->get_address_fields( $this->get_value('billing_country'), 'billing_' );
$this->checkout_fields['shipping']   = $woocommerce->countries->get_address_fields( $this->get_value('shipping_country'), 'shipping_' );
$this->checkout_fields['account']    = array(
    'account_username' => array(
        'type' => 'text',
        'label' => __('Account username', 'woocommerce'),
        'placeholder' => _x('Username', 'placeholder', 'woocommerce')
    'account_password' => array(
        'type' => 'password',
        'label' => __('Account password', 'woocommerce'),
        'placeholder' => _x('Password', 'placeholder', 'woocommerce'),
        'class' => array('form-row-first')
    'account_password-2' => array(
        'type' => 'password',
        'label' => __('Account password', 'woocommerce'),
        'placeholder' => _x('Password', 'placeholder', 'woocommerce'),
        'class' => array('form-row-last'),
        'label_class' => array('hidden')
$this->checkout_fields['order']  = array(
    'order_comments' => array(
        'type' => 'textarea',
        'class' => array('notes'),
        'label' => __('Order Notes', 'woocommerce'),
        'placeholder' => _x('Notes about your order, e.g. special notes for delivery.', 'placeholder', 'woocommerce')

This array is also passed through a filter:

$this->checkout_fields = apply_filters('woocommerce_checkout_fields', $this->checkout_fields);

This means you have full control over checkout fields – you just need to know how to access them!

Lesson 1 – Overriding core fields ↑ Back to Top

Hooking into the  woocommerce_checkout_fields filter will let you override any field. As a demonstration, lets try changing the placeholder on the order_comments fields. Currently its set to:

_x('Notes about your order, e.g. special notes for delivery.', 'placeholder', 'woocommerce')

We can change this by adding a simple function to our theme functions.php file:

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
     $fields['order']['order_comments']['placeholder'] = 'My new placeholder';
     return $fields;

Thats it! You can override other parts too, like the labels:

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
     $fields['order']['order_comments']['placeholder'] = 'My new placeholder';
     $fields['order']['order_comments']['label'] = 'My new label';
     return $fields;

You can even remove fields entirely:

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {

     return $fields;

Heres a full list of fields in the array passed to woocommerce_checkout_fields:

  • billing
    • billing_first_name
    • billing_last_name
    • billing_company
    • billing_address_1
    • billing_address_2
    • billing_city
    • billing_postcode
    • billing_country
    • billing_state
    • billing_email
    • billing_phone
  • shipping
    • shipping_first_name
    • shipping_last_name
    • shipping_company
    • shipping_address_1
    • shipping_address_2
    • shipping_city
    • shipping_postcode
    • shipping_country
    • shipping_state
  • account
    • account_username
    • account_password
    • account_password-2
  • order
    • order_comments

Each field contains an array of properties:

  • type – type of field (text, textarea, password, select)
  • label – label for the input field
  • placeholder – placeholder for the input
  • class – class for the input
  • required – true or false, whether or not the field is require
  • clear – true or false, applies a clear fix to the field/label
  • label_class – class for the label element
  • options – for select boxes, array of options (key => value pairs)

In some specific cases you will need to use the woocommerce_default_address_fields filter. This filter is applied to all billing and shipping default fields:

  • country
  • first_name
  • last_name
  • company
  • address_1
  • address_2
  • city
  • state
  • postcode

For example to make the address_1 field not required:

// Hook in
add_filter( 'woocommerce_default_address_fields' , 'custom_override_default_address_fields' );

// Our hooked in function - $address_fields is passed via the filter!
function custom_override_default_address_fields( $address_fields ) {
     $address_fields['address_1']['required'] = false;

     return $address_fields;

Defining select options

If you are adding a field with type ‘select’, as stated above you would define key/value pairs. Example:

$fields['billing']['your_field']['options'] = array(
  'option_1' => 'Option 1 text',
  'option_2' => 'Option 2 text'

Lesson 2 – Adding custom shipping and billing fields ↑ Back to Top

Adding fields is done in a similar way to overriding fields. As an example, lets add a new field to the shipping fields – shipping_phone

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
     $fields['shipping']['shipping_phone'] = array(
        'label'     => __('Phone', 'woocommerce'),
    'placeholder'   => _x('Phone', 'placeholder', 'woocommerce'),
    'required'  => false,
    'class'     => array('form-row-wide'),
    'clear'     => true

     return $fields;
It's alive!

It’s alive!

Now we have this new field, what do we do with it? We don’t need to do anything! Because we’re defined the field in the checkout_fields array the field will be automatically processed and saved to the order post meta (in this case the field will be _shipping_phone). If you want to add extra validation rules see the checkout class; there are additional hooks in there you can use.

Lesson 3 – Adding a custom special field ↑ Back to Top

We’ve covered adding fields to billing/shipping/checkout fields but what if we want something more custom? Lets add a new field to the checkout, we can add this new field after order notes by hooking into the following:

This will give us:

WooCommerce Codex - Checkout Field Hook

Next we need to validate the field when the checkout form gets posted. For the sake of this example we’ll make it required:

This will show an error on the checkout if the field is left blank:

WooCommerce Codex - Checkout Field Notice

Finally, lets save the new field to the order custom fields using the following code:

Sorted! The field will now be saved to the order.

And one more thing, if you wish to display the custom field value on the admin order edition page, you can add this code:

Here is the result:


Example – Make phone number not required

Lesson 4 – Adding Custom Fields to Emails ↑ Back to Top

To add a custom field value to WooCommerce emails (completed order email for example), use the following snippet:

* Add the field to order emails
add_filter('woocommerce_email_order_meta_keys', 'my_custom_checkout_field_order_meta_keys');

function my_custom_checkout_field_order_meta_keys( $keys ) {
$keys[‘My Field 1′] = ‘_my_field_1;
$keys[‘My Field 2′] = ‘_my_field_2′;
return $keys;


Back to the top