Skip to content
Ecom

WooCommerce Checkout Blocks vs Shortcodes: The 2026 Reality

The blocks based checkout is no longer experimental, but migrating from the shortcode still breaks things. Here is when to migrate, what breaks, and the theme edits we always redo afterwards.

By WitsCode9 min read

For a while the block based Cart and Checkout felt like an experiment you could ignore. That window has closed. The blocks have been generally available since WooCommerce 7.8 in mid 2023, they became the default for new stores with 8.3, and by 2026 every new feature that ships from the core team is block first. Apple Pay express buttons, the Local Pickup block, inline address autocomplete, the new order summary patterns, none of them ever land on the shortcode checkout.

That does not mean every store should migrate tomorrow. We run and maintain more than 250 WordPress sites at WitsCode, a lot of them WooCommerce, and we still have a healthy chunk on [woocommerce_checkout]. Some of them should stay there for now. The rest need a plan, because the migration is not a button click. It is a set of hook rewrites, a template purge, and a payment gateway audit.

This article walks through what actually changes under the hood, which extensions are still holdouts, the theme files that silently stop doing anything, and the Store API plus nonce plumbing that caching plugins love to break.

The architecture changed, not just the markup

The shortcode checkout is a server rendered PHP page. It posts to admin-ajax.php with the action woocommerce_checkout, refreshes fragments via wc-ajax=update_order_review, and lets you filter fields through woocommerce_checkout_fields, woocommerce_billing_fields, and woocommerce_default_address_fields. Anything registered with jQuery events like updated_checkout or checkout_place_order hangs off that flow.

The block checkout is a React application that talks to the WooCommerce Store API at /wp-json/wc/store/v1/cart and /wp-json/wc/store/v1/checkout. It authenticates with a nonce in the X-WC-Store-API-Nonce header plus a Cart Token (a JWT since 8.3) in a Cart-Token header for guest carts. The body is JSON. There is no admin-ajax involved in the primary flow.

This is the single most important thing to understand before you migrate. Every bit of code that listened for $(document.body).on('updated_checkout', ...) will go quiet. It will not error. It will simply never fire again. That includes analytics pixels, address lookup widgets, cross-sell modals, and a surprising amount of custom validation. Audit your theme and your mu-plugins for those events before you touch anything.

Hook migration: from filters to schema extensions

The filter you probably used most, woocommerce_checkout_fields, has no effect on the block checkout. To add a custom field you have two paths.

The lightweight path, introduced in WooCommerce 8.6, is woocommerce_blocks_register_checkout_field. You pass it an ID, a label, a location (contact, address, or order), and a validation callback. The block renders it for you, validates on the server, and stores the value on the order. For a tax ID field or a delivery note, that is enough.

The heavier path is a full schema extension. You register data against the cart or checkout endpoint with woocommerce_store_api_register_endpoint_data, you handle validation and persistence through woocommerce_store_api_checkout_update_order_from_request, and you ship a small JS bundle that calls registerCheckoutBlock from @woocommerce/blocks-checkout. That JS bundle is what places your field inside the block tree and wires it to the cart state.

Validation no longer runs through woocommerce_checkout_process. You either pass a callback into the registration function or hook the schema validation. Saving meta is no longer woocommerce_checkout_update_order_meta, it is woocommerce_store_api_checkout_update_order_from_request, and the payload is already decoded for you.

Payment gateways have their own migration. A custom gateway that only extended WC_Payment_Gateway will appear in the block UI, but the submit often fails because its JS assumed admin-ajax. You need to implement AbstractPaymentMethodType, register a JS handler, and declare support via woocommerce_blocks_payment_method_type_registration. This is the piece that catches most in-house gateways and older third-party ones.

The extension compatibility matrix, as of April 2026

Extension support has improved a lot, but there are still real holdouts. Here is where the common ones stand.

WooCommerce Subscriptions works on block checkout for straightforward new purchases and renewals since the 6.x line. Mixed carts with a sign-up fee plus a one-time product still fall back to classic on many setups, and the recurring total display depends on a custom inner block that some themes style poorly.

WooCommerce Bookings is partially there. The date and time picker has a block version, but bookings that use persons or resources still push the site back to the classic checkout. If your bookings are simple date windows, you are fine.

WooCommerce Deposits from Webtomizer is still shortcode only as of 4.2. If your business model depends on deposits, you keep classic checkout for now. There is no workaround worth shipping.

The official Gift Cards extension has been block compatible since 1.16. The official Checkout Field Editor was rewritten in 2.0 to emit block fields, but any legacy export file from 1.x has to be rebuilt by hand. Do not expect to import the old JSON.

On the gateway side, Stripe, PayPal, Klarna, and Square are all block native. Authorize.Net AIM is still shortcode only at the time of writing. One Page Checkout from SkyVerge is not block compatible and, frankly, is not going to be. CheckoutWC, the premium replacement, works with either and tends to be the pragmatic answer when you need a custom layout in a hurry.

Before you migrate a client, pull the active plugin list and walk it against this matrix. If a deal breaker shows up, the answer is either a substitute plugin, a custom block extension, or stay on shortcode until the vendor catches up.

The theme files that silently stop working

This is the category that trips up the most developers, because WooCommerce does not warn you. Your overrides still exist, WooCommerce just never loads them.

The templates that become inert on block checkout are woocommerce/checkout/form-checkout.php, form-billing.php, form-shipping.php, review-order.php, the cart pair cart/cart.php and cart/cart-totals.php, and the mini cart template cart/mini-cart.php. Custom woocommerce.php wrappers that filter the_content still run, but any markup they inject into the checkout area is ignored because the block renders its own container.

The thank you page is a useful exception. checkout/thankyou.php still works, because the order received page is not block driven yet. That is where your conversion pixels and post-purchase messaging still live. Keep that override.

CSS is the other silent killer. Selectors that target #customer_details, .woocommerce-checkout-review-order-table, .shop_table.woocommerce-checkout-review-order-table match nothing on the block checkout. The block uses its own classes prefixed with .wc-block-checkout, .wc-block-components-*, and .wp-block-woocommerce-checkout-*. Our standard migration deliverable always includes a stylesheet pass where we port the brand accents, the button styles, and the error state colors onto the new class names. It takes an hour or two and it is the difference between a checkout that looks like Wix defaults and a checkout that looks like the store.

A clean migration usually means deleting the dead overrides rather than leaving them. If the team later switches a product type and re-enables classic checkout, they will be confused to find stale templates taking effect. Remove what no longer applies and document what remains.

Store API, nonces, and why caching plugins break checkout

The Store API is a REST surface, and like any REST surface it has rules your infrastructure has to respect. Two of them bite hard in production.

The first is nonce handling. The default nonce lifetime is twelve hours. On long browsing sessions the block refreshes the nonce through a Nonce or Nonce-Timestamp response header. If a caching layer strips those headers, or a CDN rule normalizes them out, the block cannot update the nonce and the checkout button quietly fails. We have debugged this three times in the last year. Cloudflare APO, WP Rocket full page cache, and one enterprise reverse proxy were the culprits. The fix is the same each time: exclude /wp-json/wc/store/ from caching entirely, and never cache any page that contains the checkout or cart block.

The second is the Cart Token. Guest carts are now keyed by a JWT stored alongside the legacy wc_cart_hash cookie and sent in a Cart-Token header. If your security hardening plugin forces HttpOnly on every cookie, the JS cannot read the token and the cart appears empty on every page load. You need the Cart Token cookie to be readable by JS.

For headless setups, add Nonce, Nonce-Timestamp, and Cart-Token to your Access-Control-Expose-Headers. Otherwise the front end cannot see the values the API is trying to hand back.

The checkout edits we always redo after migration

Every migration we do ends with roughly the same punch list. If you are planning one, pre-plan these so they do not surprise the client.

A custom terms and conditions checkbox that used to hook woocommerce_review_order_before_submit is gone. The replacement is either the built-in terms and conditions block, which points at a WordPress page, or a slot fill via registerCheckoutBlock that renders an inline consent checkbox. Pick one and move the content accordingly.

Analytics pixels that fired on jQuery events need to be rewired. GA4 conversion on the thank you page still works through checkout/thankyou.php and the woocommerce_thankyou action. Meta Pixel AddPaymentInfo and InitiateCheckout no longer fire because the events do not exist. Use the Store API action woocommerce_store_api_checkout_order_processed on the server for Conversions API, or subscribe to the block data store on the client if you need in-page events.

Address autocomplete widgets from Loqate, Google Places, or similar all need their block-ready build. The classic scripts do nothing. Most vendors now ship both, but you have to enable the block integration explicitly.

The order notes field is still available, but the block places it behind a disclosure by default. Merchants often think it has been removed. Show them where it is, or use the inner block API to promote it to always visible if the business actually uses it.

The coupon field is collapsed behind a "Have a coupon?" link. If coupons are a meaningful part of the funnel, consider a layout override that keeps the field open, and A/B test it. On a couple of client stores we saw coupon redemption rise by double digits after uncollapsing that field, which matters either way, but especially if the marketing team is running paid traffic to a coupon code.

When to migrate, when to wait

Migrate now if you meet all of these: your gateway is block native, your extensions in the compatibility matrix are green, your checkout customisations are shallow, and your mobile checkout is slow. In that scenario the blocks will give you a faster Time to Interactive on mobile, usually two to six hundred milliseconds, mostly because jQuery fragment reloads are gone. That alone is worth a three to eight percent lift in mobile checkout completion on the stores we have measured.

Wait if you depend on Deposits, non-simple Bookings, Authorize.Net AIM, or One Page Checkout, or if your theme has heavy custom PHP overrides that a stakeholder is attached to. The shortcode is still maintained, it is not going to break, and a forced migration in that situation turns into a three-week project that nobody budgeted for.

If you are somewhere in between, the order we recommend is: pilot on a staging clone, run the real traffic through a percentage test using a caching-aware split, rebuild the theme overrides, rewire the pixels, and only then flip production. Keep a rollback plan, because WooCommerce lets you swap the page content back to the shortcode in seconds, which has saved our bacon twice.

Where WitsCode fits

Most of our block migration engagements land on teams that already tried the swap, saw a pixel break or a plugin fall over, reverted, and now want someone to do it properly. We audit the plugin stack against the compatibility matrix, rewrite the custom fields and validation into the schema extension pattern, port the theme styling onto the new class names, and put the Store API and Cart Token rules into the caching config so nothing silently breaks at 2am.

If you are staring at a [woocommerce_checkout] shortcode wondering whether to touch it, that is the work. It is not glamorous but it is the last mile between "we have a modern checkout" and "our checkout actually converts." Get it done once, document it, and it stays done.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

Shopify Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss ecom for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.