CHECKOUT · FUNCTIONS · SHOPIFY PLUS · TECHNICAL · 15 JULY 2025 · 9 MIN READ
Checkout UI extensions: what belongs in checkout and what does not
Extensions let you put almost anything into checkout. Almost everything people put there costs more conversion than it earns.
Checkout UI extensions let you add UI and logic to any step of Shopify checkout, in a sandbox that cannot touch the checkout page itself or payment data, using Shopify’s own components. What belongs there is information a buyer needs in order to complete this order and cannot get anywhere else: a delivery date, a gift message, a PO number, an age or licence confirmation, a compliance consent. What does not belong is anything you could have shown on the product or cart page — upsells, newsletter signups, chat widgets, banners — because checkout is the one page where the buyer has already decided and every element you add is a chance to un-decide. And rules that must hold should be a validation Function, not a UI extension, because a Function runs server-side and a client-side check does not survive express checkout.
IN SHORT
- Extensions run in an isolated sandbox with no access to the checkout page’s HTML or assets, no access to sensitive payment information, and only the components and APIs the platform exposes.
- A compiled checkout UI extension bundle cannot exceed 64 KB, per Shopify’s documentation — which is a design constraint, not a limitation to work around.
- Extensions come in three shapes: block targets a merchant positions in the checkout editor, static targets at fixed locations, and runnable targets that provide logic with no UI.
- A merchant can place up to three extensions in the same block target location, which is the practical ceiling on how crowded one spot can get.
- Adding a field is easy; enforcing a rule is a different mechanism. Cart and checkout validation Functions run server-side and cover accelerated checkouts, and a store can activate a maximum of 25 of them.
- Only Shopify Plus stores can add app blocks to the information, shipping and payments pages; every plan can customise the Thank you and Order status pages.
- Four capabilities must be declared explicitly in `shopify.extension.toml`: `api_access`, `network_access`, `block_progress` and `collect_buyer_consent`.
What an extension actually is, and what the sandbox means
A checkout UI extension is a small component that Shopify renders inside checkout at a defined location, built from Shopify’s own component set. It does not run in the checkout page. The documentation is direct about this: extensions "run in an isolated sandbox, separate from the checkout page and other UI extensions", they "don’t have access to sensitive payment information or the checkout page itself (HTML or other assets)", and they are "limited to specific UI components and APIs that are exposed by the platform".
That is the trade, and it is a good one. You give up arbitrary DOM access — the thing checkout.liquid scripts relied on — and in return Shopify can change checkout underneath you without breaking your customisation, and can guarantee a floor on performance and PCI scope. The cost is that "we want checkout to look like our brand" and "we want to inject a third-party widget" are no longer things you can simply do.
The targets come in three shapes. Block targets are, per the documentation, "flexible placement targets that merchants can position using the checkout and accounts editor" — the merchant decides where your thing appears. Static targets "appear at fixed locations in checkout, such as before actions, after contact fields, or after cart line items". Runnable targets "provide data or functionality without rendering UI components", which is where logic with no visible output goes. A merchant can add up to three extensions to the same block target location, so the spot you are designing for may already have two neighbours.
Data comes from a shopify global — shopify.buyerIdentity, shopify.cost, and mutation-style calls such as applyAttributeChange and applyMetafieldChange to write back to the order. Anything beyond that is a declared capability, covered below.
The 64 KB budget is the most useful constraint in the API
Shopify documents a hard ceiling: "Your compiled UI extension bundle can’t exceed 64 KB, or 128 KB for full-page customer account extensions."
Teams treat that as an obstacle. It is the best thing about the API. Sixty-four kilobytes of compiled JavaScript will not accommodate a charting library, a date-picker dependency tree or an analytics SDK, which means it will not accommodate most of what has historically been bolted onto checkout. The limit enforces the judgement that this post is arguing for: if your checkout addition does not fit in 64 KB, the question to ask is not how to shrink it but whether it belongs in checkout at all.
It also keeps the failure mode honest. A limit you hit in the build is a conversation with your developer. A limit that did not exist is a checkout that got 400 KB slower over two years and nobody can say which app did it.
What belongs in checkout
One test: does the buyer need this to complete *this* order, and is checkout the first place they could possibly have provided or seen it? If the answer to either half is no, it goes somewhere else.
- A delivery date or delivery window tied to the address and method the buyer has just chosen. This genuinely cannot be shown earlier, because it depends on data that only exists at this step.
- Fields the order cannot be fulfilled without: a gift message, delivery instructions, a required installation date, a PO number for a B2B buyer, a tax or customs identifier for a regulated destination.
- Confirmations with a legal weight — age, licence, prescription, restricted-goods acknowledgement — paired with a server-side rule that enforces them.
- Consent you are obliged to collect at this point, including marketing opt-in, which has its own declared capability (
collect_buyer_consent) precisely because it is a sensitive category. - A short, factual reassurance the buyer is looking for at this exact moment: the returns window, the fact that duties are included, who to ring. One line, not a panel.
What does not, and where it should go instead
Everything below can be built. We are arguing that it should not be, and in most cases there is a better home for it.
Upsells and cross-sells. The most requested checkout extension, and the one we push back on most. The buyer has committed; the job of the page is to get out of the way. If the offer is good, it belongs on the product page, in the cart, or in the post-purchase page after payment is taken, where a decline costs nothing. A checkout upsell that adds friction to 100% of sessions to convert a small fraction of them is a trade worth measuring before shipping, not after.
Newsletter signups and account creation prompts. Ask afterwards. Every field on this page competes with the one field you need them to finish.
Live chat. A chat widget in checkout is an invitation to stop checking out and start a conversation. Put support contact details in text instead, and if buyers genuinely get stuck at a step, fix the step.
Marketing banners and countdown timers. Manufactured urgency at the point of payment reads as pressure, and in some jurisdictions the claim underneath it is a regulatory problem.
Analytics and tag injection. This is not what UI extensions are for. Web pixel extensions exist for exactly this and run in their own sandbox, which is why they are the supported route rather than the workaround.
Brand restyling. Extensions cannot restyle the checkout page, by design. Checkout branding is its own controlled surface; an extension that fights the host page’s visual language just looks like a third-party insert, because that is what it is.
A field is not a rule: use a validation Function
This is the mistake with the highest cost, and it is an architectural one rather than a matter of taste.
A UI extension can block progress if you declare the block_progress capability, and that is the right tool for guiding a buyer: show the error next to the field, explain what to change. But it is a client-side check inside a sandbox, on a page that can be bypassed entirely. Express checkouts — Shop Pay, PayPal, Apple Pay, Google Pay — do not walk the buyer through your extension’s step.
Rules that must hold belong in a cart and checkout validation Function. Shopify documents it as running server-side across cart interaction, checkout interaction and checkout completion, and as covering accelerated checkouts. The documented use cases are exactly the ones people try to implement in the UI layer: minimum and maximum quantities, B2B order limits, address restrictions such as no PO boxes, age verification, tokengated eligibility, required purchase order numbers and localised tax fields. Errors can be attached to a specific checkout field using JSONPath, so the server-side rule and the field-level message are the same mechanism rather than two implementations that drift apart.
Two documented limits worth knowing before you design around it. A store can activate a maximum of 25 validation functions. And the surface coverage is not universal: the documentation lists validation as supported for B2B, cart, checkout, draft orders, storefront and accelerated checkout, and not supported for the Create Order API, order editing, POS, pre-orders or subscriptions. If your rule must also hold for a subscription renewal or an order an agent creates in the admin, the Function alone will not get you there and the rule needs enforcing further back.
The pattern we build: the Function is the rule, the UI extension is the explanation. Skip the Function and you have a rule that holds for buyers who use the slow path and evaporates for the ones who tap Shop Pay.
Who can do what, and the deadline sitting behind all of this
Two practical constraints shape most of these conversations before design starts.
Plan. Adding app blocks to the checkout page itself — information, shipping and payments — requires Shopify Plus. Every plan can customise the Thank you and Order status pages. So if you are not on Plus, the honest scope is post-purchase, and the good news is that post-purchase is where several of the things we just talked you out of putting in checkout actually belong.
The migration deadline. checkout.liquid is already unsupported for the information, shipping and payment steps. Shopify’s timeline sunsets checkout.liquid and additional scripts for the Thank you and Order status pages on 28 August 2025. If anything is still running there — an analytics snippet, a referral pixel, a survey — it needs an extension-based replacement before that date, and the inventory of what is in those boxes is usually longer than anyone remembers.
That inventory is the job worth doing this month, and it is also the opportunity: a forced migration is the cheapest moment to not re-implement the three things nobody could justify in the first place. We scope [Shopify builds](/services/build) the same way — the checkout you migrate should be smaller than the one you had.
The order we would build in
For any checkout customisation request, five steps, and most requests stop at step two.
- Check whether a native setting already does it. Delivery customisation, duties display, store credit and discount behaviour have all moved into the platform, and a setting beats an extension every time.
- Ask what breaks if this is not in checkout. If the answer is "conversion might be slightly lower", it belongs on the cart or the post-purchase page.
- If it is a rule, write the validation Function first and make it the source of truth.
- Add the UI extension as the explanation of the rule, or as the single field the order genuinely needs, and declare only the capabilities it uses —
api_access,network_access,block_progress,collect_buyer_consent— because each one is a question a reviewer and a merchant will reasonably ask about. - Measure the step, not the site. Checkout completion rate, per step, before and after. Any checkout addition that cannot show it paid for its own friction should come back out.
Questions this raises
What can you add to Shopify checkout with UI extensions?
Custom UI and logic at defined locations in any checkout step, built from Shopify’s component set: fields such as gift messages, delivery instructions or a PO number; a delivery date based on the chosen address and method; age or licence confirmations; required consents; and short factual reassurance. What you cannot do is restyle the checkout page, reach into its HTML, read payment details or inject third-party scripts — extensions run in an isolated sandbox with only the APIs the platform exposes.
Do you need Shopify Plus for checkout UI extensions?
For the checkout page itself, yes. Adding app blocks to the information, shipping and payments pages requires Shopify Plus. Stores on other plans can customise the Thank you and Order status pages, which is where post-purchase offers, surveys and tracking belong anyway.
How big can a checkout UI extension be?
Shopify’s documentation states a compiled bundle cannot exceed 64 KB, or 128 KB for full-page customer account extensions. Treat it as a design constraint rather than an obstacle: anything that needs a large dependency to work is almost certainly not something that should be loading inside checkout.
Should checkout validation be a UI extension or a Function?
A Function, with a UI extension to explain it. A UI extension with the `block_progress` capability runs client-side in a sandbox and is not part of an express checkout flow, so a buyer using Shop Pay or Apple Pay may never encounter it. A cart and checkout validation Function runs server-side across cart, checkout and checkout completion, including accelerated checkouts, and can attach its error to a specific field.
Are checkout upsells a good idea?
Usually not in checkout. Every element you add is friction for every buyer, and these are the buyers who have already decided. Put the offer on the product page, in the cart, or on the post-purchase page after payment has been taken — where a decline costs nothing. If you do test one in checkout, measure completion rate per step and be willing to remove it.
What replaces additional scripts and checkout.liquid?
Checkout UI extensions for UI, Functions for rules, and web pixel extensions for analytics and tracking. `checkout.liquid` is already unsupported for the information, shipping and payment steps, and Shopify’s timeline sunsets it and the additional scripts box for the Thank you and Order status pages on 28 August 2025, so anything still living there needs an extension-based replacement.
How many extensions can appear in the same place in checkout?
Up to three in the same block target location, positioned by the merchant in the checkout editor. Worth remembering when designing one: your extension may be sharing that space with two others, so it should read sensibly at any position and should not assume it is the only thing there.
NEXT STEP
Free store audit
A senior Shopify engineer reviews your storefront, theme performance and checkout, then sends a prioritised list of fixes.
