The WooCommerce checkout form covers the standard fields required for most orders: billing and shipping address, contact details, and payment method. For many stores, these defaults are sufficient. But real-world e-commerce often demands additional information from the customer at the point of purchase that WooCommerce does not collect by default. A gift shop might need a personal message for the recipient. A food delivery service needs a delivery time preference. A custom printing company needs artwork instructions. A B2B store needs a company VAT number for invoice compliance. A clothing retailer might offer monogramming and needs the initials. In all of these cases the checkout form is the right place to collect the information because the customer is already filling in a form and the data naturally belongs to that specific order. WooCommerce provides a well-documented set of action and filter hooks specifically for customizing the checkout form without modifying core files. The woocommerce_after_order_notes action renders markup inside the checkout form after the order notes textarea — a clean location for a custom field. The woocommerce_checkout_process action fires when the form is submitted and is the correct place to validate the field’s value before the order is created. The woocommerce_checkout_update_order_meta action fires after a successful order creation and is where you save the field value as order meta. Once saved as order meta, the value is accessible in the order admin view, in order confirmation emails via the woocommerce_email_order_meta hook, and through the REST API. The three hooks together form a complete cycle: render, validate, save. The field type you output in the first hook can be any standard HTML input — text, textarea, select, checkbox, or radio — and WooCommerce’s built-in woocommerce_form_field() helper function generates properly styled, validated markup consistent with the active checkout theme without writing raw HTML. This is the approach used by most WooCommerce checkout customization plugins internally, and doing it directly in functions.php eliminates the plugin overhead. For a store that also needs to redirect customers to a custom page after placing an order, this pairs well with controlling WooCommerce redirect behavior during the shopping session.
Problem: WooCommerce checkout does not include a custom field you need to collect from customers, such as a gift message or VAT number.
Solution: Add the following code to your functions.php file:
<?php
// 1. Display the custom field on the checkout page
add_action( 'woocommerce_after_order_notes', 'ha_add_gift_message_field' );
function ha_add_gift_message_field( $checkout ) {
woocommerce_form_field( 'ha_gift_message', array(
'type' => 'textarea',
'class' => array( 'form-row-wide' ),
'label' => __( 'Gift Message (optional)', 'woocommerce' ),
'placeholder' => __( 'Write a personal message for the recipient...', 'woocommerce' ),
'required' => false,
), $checkout->get_value( 'ha_gift_message' ) );
}
// 2. Validate the field on form submission (if required)
add_action( 'woocommerce_checkout_process', 'ha_validate_gift_message_field' );
function ha_validate_gift_message_field() {
// Uncomment to make the field required:
// if ( empty( $_POST['ha_gift_message'] ) ) {
// wc_add_notice( __( 'Please enter a gift message.', 'woocommerce' ), 'error' );
// }
}
// 3. Save the custom field value to order meta
add_action( 'woocommerce_checkout_update_order_meta', 'ha_save_gift_message_field' );
function ha_save_gift_message_field( $order_id ) {
if ( ! empty( $_POST['ha_gift_message'] ) ) {
update_post_meta(
$order_id,
'_ha_gift_message',
sanitize_textarea_field( wp_unslash( $_POST['ha_gift_message'] ) )
);
}
}
// 4. Display the value in the order admin view
add_action( 'woocommerce_admin_order_data_after_billing_address', 'ha_display_gift_message_in_admin' );
function ha_display_gift_message_in_admin( $order ) {
$message = get_post_meta( $order->get_id(), '_ha_gift_message', true );
if ( $message ) {
echo '<p><strong>' . __( 'Gift Message', 'woocommerce' ) . ':</strong> ' . esc_html( $message ) . '</p>';
}
}
NOTE: Always sanitize user input before saving to the database. Use sanitize_text_field() for single-line inputs and sanitize_textarea_field() for multiline text, as shown above. The wp_unslash() call strips the magic quotes that PHP may add to $_POST data on some server configurations before sanitization. The leading underscore in the meta key _ha_gift_message hides it from the standard Custom Fields meta box in the post editor, keeping the admin interface clean.