Block and classic cart and checkout
The Cart and Checkout pages come in two variants. Classic ones are rendered by shortcodes and PHP templates. Block ones are rendered by editor blocks that JavaScript draws in the browser. New shops get the block variant by default, and the variant decides which hooks fire on the page.
How to tell the variant
Open Pages → Cart in the editor:
| What is on the page | Variant |
|---|---|
| A Cart block with nested blocks | Block |
The [woocommerce_cart] shortcode | Classic |
| A Checkout block | Block |
The [woocommerce_checkout] shortcode | Classic |
In code the same is checked by the WordPress function has_block():
// wp-content/plugins/acme-shop/includes/checkout.php
function acme_is_block_checkout(): bool
{
return has_block('woocommerce/checkout', wc_get_page_id('checkout'));
}The function returns true if the checkout page holds the block. Create the includes/checkout.php file and include it in acme-shop.php — it will be needed in the next chapter.
What works in both variants
Calculations run on the server the same way, so the calculation hooks are shared:
| Hook | What it does |
|---|---|
woocommerce_before_calculate_totals | Changes prices in the cart |
woocommerce_cart_calculate_fees | Adds fees and discounts |
woocommerce_package_rates | Changes shipping costs |
woocommerce_add_to_cart_validation | Validates adding to the cart |
woocommerce_add_cart_item_data | Stores cart item data |
woocommerce_order_status_... | Reacts to an order status |
woocommerce_thankyou | The Order received page |
What works only in the classic variant
Everything that prints HTML or belongs to the PHP checkout form:
| Hook | Replacement in the block variant |
|---|---|
woocommerce_before_cart, woocommerce_after_cart_table | Ordinary blocks in the page editor |
woocommerce_checkout_fields | woocommerce_register_additional_checkout_field() |
woocommerce_after_checkout_validation | Validation in the field definition |
woocommerce_checkout_order_processed | woocommerce_store_api_checkout_order_processed |
Copies of cart/ and checkout/ templates | Not used |
In the block variant these hooks are not called and raise no errors — the code is simply silent.
Going back to the classic variant
If the shop's customizations rely on classic hooks, the page is switched. Open Pages → Checkout, delete the Checkout block and insert a Shortcode block with the text:
[woocommerce_checkout]Do the same for the cart with the [woocommerce_cart] shortcode. After saving, the page is rendered by PHP templates and all hooks from old examples start working.
Plugin compatibility
A plugin can state that it has been tested with block pages — next to the HPOS declaration in the main file:
// wp-content/plugins/acme-shop/acme-shop.php
add_action('before_woocommerce_init', function () {
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
'cart_checkout_blocks',
__FILE__,
true
);
}
});A third argument of false means the opposite: the plugin is incompatible with blocks, and the page editor warns the shop owner about it.
What to choose
| Situation | Variant |
|---|---|
| A new shop, few customizations | Block |
| Many PHP fields and validations of your own | Classic |
| Payment or shipping plugins without block support | Classic |
In this course the checkout examples are written for the classic variant, and a replacement is given for the block one.
Common mistake
Code from a ten-year-old article is attached to woocommerce_before_cart or woocommerce_checkout_fields, and nothing changes on the page. There are no errors in the log, so people hunt for a typo. First check whether the page is block or classic — nine times out of ten that is the cause.