- What Is woocommerce_form_field()?
- Function Signature and Parameters
- Basic Usage
- Add a Custom Checkout Field
- Common Mistakes
- FAQs About woocommerce_form_field()
- Does woocommerce_form_field() Echo or Return HTML?
- Can I Use It Outside Checkout?
- Why Is My Field Missing From the Order?
- Does It Work With the Block Checkout?
- Conclusion
Customizing WooCommerce forms usually leads to the same question: how do I add a field that actually renders, validates, and saves? The answer is almost always woocommerce_form_field(), the helper WooCommerce uses to output its own checkout and account inputs.
Using the helper keeps your custom inputs consistent with the rest of the form. This guide covers its parameters, shows how to add a gift message field to checkout, and points out the mistakes that break saving.
What Is woocommerce_form_field()?
It is a global WooCommerce function that outputs one complete form field: the label, the input, the description, and the error wrapper. WooCommerce calls it throughout the checkout, My Account address, and registration forms, so using it gives your fields the same markup and CSS classes as the built-in ones.
Function Signature and Parameters
The function takes three arguments:
- $key is the field name. It also becomes the key you read from
$_POST. - $args is an array of settings that control how the field looks and behaves.
- $value is optional and pre-fills the input, usually from the checkout or user object.
Key Arguments
These are the arguments you will use most often:
| Argument | Purpose | Example |
|---|---|---|
type | Input type: text, email, tel, select, radio, checkbox, textarea, password, country, or state | 'textarea' |
label | Visible label text | __( 'Gift message', 'textdomain' ) |
required | Adds the required marker and enforces a non-empty value | false |
class | Wrapper classes. form-row-wide spans the full width | array( 'form-row-wide' ) |
placeholder | Placeholder text inside the input | 'Happy birthday, Jane!' |
description | Help text below the input | 'Printed on a card. 200 characters max.' |
options | Key/value pairs for select and radio fields | array( 'a' => 'A' ) |
validate | Built-in validators such as phone, email, or postcode | array( 'phone' ) |
custom_attributes | Extra HTML attributes on the input, such as maxlength | array( 'maxlength' => 200 ) |
return | Returns the HTML instead of echoing it when true | true |
priority | Sort order within the form | 120 |
Basic Usage
By default, the function echoes the markup. Pass 'return' => true when you need the HTML as a string, for example to combine it with other output.
<?php
woocommerce_form_field(
'order_gift_message',
array(
'type' => 'textarea',
'label' => __( 'Gift message', 'textdomain' ),
'required' => false,
'class' => array( 'form-row-wide' ),
'placeholder' => __( 'Happy birthday, Jane!', 'textdomain' ),
'custom_attributes' => array( 'maxlength' => 200 ),
)
);Tip: Use
form-row-widefor full-width fields like textareas or notes. For two fields side by side, useform-row-firstandform-row-lastso the layout matches the built-in address fields.
Add a Custom Checkout Field
A working field needs three pieces: a render hook, a validation hook, and a save hook. Place the snippets in a child theme’s functions.php or a site-specific plugin. The example targets the classic shortcode checkout. The checkout optimization guide covers when extra fields are worth the friction. The WooCommerce checkout fields guide covers how to add, remove, relabel, and reorder fields before implementing custom field logic.
Render the Field
woocommerce_after_order_notes runs after the order notes field, so the gift message input appears at the end of the checkout form.
<?php
add_action( 'woocommerce_after_order_notes', 'andriy_add_gift_message_field' );
function andriy_add_gift_message_field( $checkout ) {
woocommerce_form_field(
'order_gift_message',
array(
'type' => 'textarea',
'label' => __( 'Gift message', 'textdomain' ),
'required' => false,
'class' => array( 'form-row-wide' ),
'custom_attributes' => array( 'maxlength' => 200 ),
),
$checkout->get_value( 'order_gift_message' )
);
}Validate the Field
woocommerce_checkout_process fires when the customer submits the order. Read the input with wp_unslash(), then add an error notice with wc_add_notice() to block submission. The maxlength attribute only limits typing in the browser, so the server-side check is still required.
<?php
add_action( 'woocommerce_checkout_process', 'andriy_validate_gift_message' );
function andriy_validate_gift_message() {
if ( empty( $_POST['order_gift_message'] ) ) {
return;
}
$message = wp_unslash( $_POST['order_gift_message'] );
if ( mb_strlen( $message ) > 200 ) {
wc_add_notice( __( 'Gift messages can be up to 200 characters.', 'textdomain' ), 'error' );
}
}Save the Field to the Order
Save the value with woocommerce_checkout_create_order, which passes the order object before it is stored. Writing through $order->update_meta_data() works with both legacy post-based storage and High-Performance Order Storage (HPOS), the default on new WooCommerce installs. sanitize_textarea_field() keeps line breaks, which sanitize_text_field() would strip.
<?php
add_action( 'woocommerce_checkout_create_order', 'andriy_save_gift_message' );
function andriy_save_gift_message( $order ) {
if ( ! empty( $_POST['order_gift_message'] ) ) {
$order->update_meta_data( '_order_gift_message', sanitize_textarea_field( wp_unslash( $_POST['order_gift_message'] ) ) );
}
}Read the value back with $order->get_meta( '_order_gift_message' ). To display it on the thank-you page, follow the approach in the order received page guide.
Common Mistakes
Most failures come from a few mismatches:
- Mismatched names. The
$keymust match the$_POSTkey you read later. A field namedorder_gift_messageis read from$_POST['order_gift_message']. - Saving too early. Writing in
woocommerce_checkout_processruns before the order exists. Usewoocommerce_checkout_create_orderinstead. - Skipping sanitization. Pass input through
sanitize_textarea_field()orwc_clean(), and escape output withesc_html(). - Expecting the block checkout to pick it up. The block checkout ignores these hooks and uses its own additional-fields API.
FAQs About woocommerce_form_field()
Does woocommerce_form_field() Echo or Return HTML?
It echoes the markup by default. Pass 'return' => true in the arguments array to get the HTML as a string instead.
Can I Use It Outside Checkout?
Yes. WooCommerce uses it on My Account address and registration forms too. Outside checkout, you still need your own save handler, such as update_user_meta() on the form submission.
Why Is My Field Missing From the Order?
The usual causes are a field name that does not match the $_POST key, or a save hook attached to the wrong action. Confirm the name in the rendered HTML, then check that the save hook runs on woocommerce_checkout_create_order.
Does It Work With the Block Checkout?
No. The block-based checkout registers extra fields through its own API, so these snippets target the classic shortcode checkout.
Conclusion
Pair woocommerce_form_field() with a validation hook and a save hook, and a custom field works end to end with the same markup WooCommerce uses for its own inputs. Keep the number of checkout fields small, because every extra field can slow down the purchase. Selling digital products only? See this hide checkout fields for virtual products guide.
Need a custom checkout or form built for your store? Hire a WooCommerce developer, or reach out to me directly.

