The mental model shift
The shortcode checkout was PHP templates and a hundred hooks. If you wanted a field moved, a label changed or a section added, there was a filter for it, and years of Stack Overflow answers to copy. The checkout block is a React application rendered from the Store API. Most of those hooks do not fire, the templates do not exist, and the Stack Overflow answers produce nothing.
What replaced them is a smaller, more deliberate set of customisation points. Knowing which exist stops hours of trying to make old approaches work.
Tier 1: the editor
Open the checkout page in the block editor and select the checkout block or its inner blocks. Without code you can:
- Reorder and remove inner blocks: contact, shipping address, billing address, shipping options, payment, order note, terms, order summary. Some are required and cannot be removed.
- Toggle fields: company, address line 2, phone. Set each to hidden, optional or required.
- Edit text: section headings, the place order button label, the terms and privacy text and links.
- Show or hide the coupon field, the order note, the "create account" option, the shipping and billing address toggle.
- Choose the layout: order summary position, whether the return-to-cart link shows.
- Style via the site editor's block styles and
theme.json: colours, typography, spacing, button styles, following the theme.
This covers most business needs, and it is where to start. A lot of "the checkout block can't do X" is X being in the sidebar under a different name.
Tier 2: Additional Checkout Fields API
Since WooCommerce 8.9, custom fields are registered in PHP with woocommerce_register_additional_checkout_field() and appear in one of three locations: contact, address (billing and shipping) or order (the additional information section).
add_action( 'woocommerce_init', function () {
if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) ) return;
woocommerce_register_additional_checkout_field( [
'id' => 'acme/delivery-instructions',
'label' => 'Delivery instructions',
'location' => 'order',
'type' => 'text',
'required' => false,
'attributes' => [ 'maxLength' => 200 ],
] );
woocommerce_register_additional_checkout_field( [
'id' => 'acme/gift-wrap',
'label' => 'Gift wrap this order',
'location' => 'order',
'type' => 'checkbox',
] );
} );Field types: text, select, checkbox. Validation via woocommerce_validate_additional_field and woocommerce_sanitize_additional_field. Values are saved automatically to the order (and to the customer for contact and address fields), shown in the admin order screen, and available via $order->get_additional_field_value( 'acme/delivery-instructions' ) for emails and fulfilment.
This is the supported way to add fields, and it replaces the old woocommerce_checkout_fields filter entirely for the block. Its limits: three field types, three locations, no conditional show/hide without JavaScript, and no fields inside the payment section.
Tier 3: JavaScript slot/fill and the Checkout Block API
For UI that is not a field, a notice, a delivery date picker, a loyalty points panel, a custom summary line, the block exposes slots you fill with React components registered from a script:
ExperimentalOrderMetaandExperimentalDiscountsMetafor content in the order summary.ExperimentalOrderShippingPackagesfor shipping package content.- Inner block areas where custom blocks can be inserted (register a block with
parent: ['woocommerce/checkout-fields-block']and it appears in the editor's inserter inside the checkout).
Registering a fill:
import { registerPlugin } from '@wordpress/plugins';
import { ExperimentalOrderMeta } from '@woocommerce/blocks-checkout';
const Render = () => (
<ExperimentalOrderMeta>
<div className="acme-eta">Estimated delivery: 2 to 3 working days</div>
</ExperimentalOrderMeta>
);
registerPlugin( 'acme-eta', { render: Render, scope: 'woocommerce-checkout' } );Data flow: extensionCartUpdate posts data to the server and returns an updated cart; ExtendSchema (the Store API's extensibility) exposes server-side data to the client. Build with @wordpress/scripts and enqueue with dependencies on the checkout block's scripts.
This tier is where real customisation lives and it requires a JavaScript build. The APIs marked Experimental have been stable in practice for a long time but the names carry a warning.
Tier 4: Store API filters (server side)
Cart and checkout data can be filtered server-side: woocommerce_store_api_cart_errors, the ExtendSchema registration for custom data on cart items and the checkout, woocommerce_store_api_checkout_update_order_from_request to act on submitted data before the order is placed, and woocommerce_store_api_checkout_order_processed after. These replace many woocommerce_checkout_* hooks and fire for both block and shortcode when the Store API is used.
Filters for rendered content exist through the __experimental_woocommerce_blocks_... and woocommerce_blocks_... registry: cart item names, prices, subtotals, and place-order button label, via registerCheckoutFilters in JavaScript.
What is not customisable
- The payment method section's internals. Each gateway renders its own UI. You cannot inject fields into it.
- Field order within a section beyond what the editor offers. Address fields follow the country's format.
- Multi-step checkout. The block is single page. Plugins that promised multi-step for the shortcode do not apply.
- Arbitrary HTML between sections without a custom block or a fill.
- Anything via the old PHP template overrides.
checkout/form-checkout.phpand friends are not used.
If a requirement is on this list, the honest options are a fill (tier 3), the shortcode checkout, or accepting the block's behaviour.
Choosing block or shortcode
The block is the default for new stores and gets the development attention; the shortcode remains supported and is more customisable in the old ways. For a store whose checkout requirements fit tiers 1 and 2, the block is the better experience. For a store with heavy legacy customisation, migrating means re-implementing it in tiers 2 to 4, which is a project, and staying on the shortcode is legitimate until that project is scheduled.
Where this sits
Deciding which tier a requirement lives in is the first step of any WooCommerce checkout work we take on, because it decides whether the answer is a sidebar toggle, a few lines of PHP, or a JavaScript build. The Additional Checkout Fields API covers more than people expect, and the fills cover most of the rest.
