WooCommerce’s Cart and Checkout blocks expose a set of registered inner block slots — named extension points where third-party plugins can inject their own blocks without forking core templates. The @woocommerce/blocks-checkout package provides the registerCheckoutBlock API and a set of named slot components (ExperimentalOrderMeta, ExperimentalDiscountsMeta, etc.) that map to these slots.
Problem: WooCommerce Blocks checkout replaces the classic checkout shortcode — custom fields, validation, and order meta added via classic hooks like woocommerce_checkout_fields no longer work, and there is no obvious replacement API.
Solution: Use the WooCommerce Blocks IntegrationInterface and register_checkout_field() API (WooCommerce 8.6+) to add fields to the contact, address, or order section. Handle server-side validation with the woocommerce_blocks_validate_location_*_fields action and save to order meta with woocommerce_blocks_checkout_order_processed.
The example below registers a custom "Delivery Instructions" text field that appears inside the Checkout block's shipping address section, saves the value as order meta, and displays it in the WooCommerce admin order screen.
// js/checkout-extension/index.js
const { registerPlugin } = wp.plugins;
const { ExperimentalOrderMeta } = wc.blocksCheckout;
const { extensionCartUpdate } = wc.blocksCheckout;
const { TextareaControl } = wp.components;
const { useState } = wp.element;
function DeliveryInstructionsSlot() {
const [ note, setNote ] = useState( '' );
const handleChange = ( value ) => {
setNote( value );
// Push the value into cart extensions so it travels to the server
extensionCartUpdate( {
namespace: 'my-plugin',
data: { delivery_note: value },
} );
};
return wp.element.createElement( ExperimentalOrderMeta, null,
wp.element.createElement( TextareaControl, {
label: 'Delivery instructions (optional)',
value: note,
onChange: handleChange,
rows: 3,
} )
);
}
registerPlugin( 'my-plugin-checkout-extension', {
render: DeliveryInstructionsSlot,
scope: 'woocommerce-checkout',
} );
<?php
// Save delivery note from cart extensions to order meta
add_action( 'woocommerce_store_api_checkout_order_processed',
function ( WC_Order $order ) {
$request = WC()->session ? WC()->session->get( 'store_api_draft_order' ) : null;
// Retrieve from cart extensions (set by extensionCartUpdate)
$cart = WC()->cart;
$extensions = $cart ? $cart->get_cart_meta( 'extensions' ) : [];
$note = sanitize_textarea_field(
$extensions['my-plugin']['delivery_note'] ?? ''
);
if ( $note ) {
$order->update_meta_data( '_delivery_note', $note );
$order->save();
}
}
);
// Display in admin order screen
add_action( 'woocommerce_admin_order_data_after_shipping_address',
function ( WC_Order $order ) {
$note = $order->get_meta( '_delivery_note' );
if ( $note ) {
echo '<p><strong>' . esc_html__( 'Delivery instructions:', 'my-plugin' ) . '</strong><br>'
. nl2br( esc_html( $note ) ) . '</p>';
}
}
);
NOTE: extensionCartUpdate() triggers a Store API cart update on every call, so debounce the handler with a short delay (200–300 ms) on onChange to avoid flooding the server with a network request for every character the user types.