← Back to notes
snippet 1 min read PHP 7.4+ WP 5.8+

How to Add a Custom Required Field to WooCommerce Checkout (and Save to Order Meta & Emails)

Add, validate, save, display, and email a required WooCommerce checkout field without installing another checkout-field plugin.

Updated Aug 24, 2026 #woocommerce #checkout #php #snippet

WooCommerce’s checkout hooks make it possible to collect one additional piece of customer information without replacing the checkout template. This example adds a required Delivery instructions field, validates it, stores it as order meta, and displays it in the admin, customer account, and order emails.

Add the snippet to a child theme’s functions.php file or use a code snippets plugin. Test it on a staging site first, especially if the store uses a checkout-field customization plugin or the WooCommerce Checkout Block.

The snippet

functions.php
// Add the field above the order notes on the classic checkout.
add_action( 'woocommerce_after_order_notes', function( $checkout ) {
woocommerce_form_field(
'delivery_instructions',
array(
'type' => 'textarea',
'class' => array( 'form-row-wide' ),
'label' => __( 'Delivery instructions', 'my-site' ),
'placeholder' => __( 'For example: leave with reception.', 'my-site' ),
'required' => true,
),
$checkout->get_value( 'delivery_instructions' )
);
} );
// Add a server-side validation rule. Browser-side "required" is not enough.
add_action( 'woocommerce_checkout_process', function() {
$value = isset( $_POST['delivery_instructions'] )
? sanitize_textarea_field( wp_unslash( $_POST['delivery_instructions'] ) )
: '';
if ( '' === $value ) {
wc_add_notice( __( 'Please add your delivery instructions.', 'my-site' ), 'error' );
}
} );
// Save the submitted value to the order.
add_action( 'woocommerce_checkout_create_order', function( $order ) {
if ( isset( $_POST['delivery_instructions'] ) ) {
$value = sanitize_textarea_field( wp_unslash( $_POST['delivery_instructions'] ) );
$order->update_meta_data( '_delivery_instructions', $value );
}
}, 10, 1 );
// Show the value in the order admin and customer order details.
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'my_site_show_delivery_instructions' );
add_action( 'woocommerce_order_details_after_order_table', 'my_site_show_delivery_instructions' );
function my_site_show_delivery_instructions( $order ) {
$value = $order->get_meta( '_delivery_instructions' );
if ( $value ) {
echo '<p><strong>' . esc_html__( 'Delivery instructions', 'my-site' ) . ':</strong><br>' . nl2br( esc_html( $value ) ) . '</p>';
}
}
// Add the value to all WooCommerce order emails.
add_filter( 'woocommerce_email_order_meta_fields', function( $fields, $sent_to_admin, $order ) {
$value = $order->get_meta( '_delivery_instructions' );
if ( $value ) {
$fields['delivery_instructions'] = array(
'label' => __( 'Delivery instructions', 'my-site' ),
'value' => $value,
);
}
return $fields;
}, 10, 3 );

How it works

woocommerce_after_order_notes renders the field on the classic checkout. The woocommerce_checkout_process callback performs validation on the server, so a request cannot bypass the requirement by disabling JavaScript. woocommerce_checkout_create_order saves the sanitized value through the CRUD order API, which works with both legacy post storage and high-performance order storage.

The leading underscore in _delivery_instructions keeps the meta key hidden from most generic custom-field interfaces. Use $order->get_meta( '_delivery_instructions' ) whenever the value is needed later.

Important limitation

This snippet targets the classic shortcode checkout. If the site uses the WooCommerce Checkout Block, use the block extensibility APIs or a block-compatible checkout-field extension instead; classic PHP checkout hooks do not automatically render fields inside the block.

For a block-based checkout, compare this approach with WooCommerce’s guide to additional checkout fields. For another checkout rule, see how to set a minimum order amount.