Checkout UI extensions vs Shopify Functions
Short answer
Checkout UI extensions add interface to checkout: fields, banners, messages, built from Shopify’s components and, for block targets, placed by the merchant in the checkout editor. Shopify Functions change checkout logic on Shopify’s servers: discounts, delivery and payment options, validation. Since API version 2026-07, blocking checkout from a UI extension is deprecated; rules should be enforced with a Cart and Checkout Validation Function. UI extensions on the information, shipping and payment steps need Shopify Plus.
- Checkout UI extensions run in an isolated sandbox and can only use the UI components and APIs Shopify exposes.
- Shopify Functions run on Shopify’s servers and see only the data their input query requests.
- Checkout UI extensions on the information, shipping and payment steps are available only on Shopify Plus.
- Merchants can add up to three extensions to the same block target in the checkout and accounts editor.
- Since version 2026-07, useBuyerJourneyIntercept and the block_progress capability are deprecated in favour of validation Functions.
Both are part of checkout extensibility, and both usually ship inside an app, so they get confused. The difference is simple: a checkout UI extension changes what the buyer sees; a Shopify Function changes what checkout does. A delivery-date picker is interface. Hiding cash on delivery for orders over a threshold is logic. Many real requirements need one of each.
Side by side
| Checkout UI extension | Shopify Function | |
|---|---|---|
| Changes | What the buyer sees and enters | Prices, options and whether checkout can proceed |
| Runs | In an isolated sandbox, separate from the checkout page and other extensions | On Shopify’s servers, as part of the checkout calculation |
| Built with | Extension target APIs and Shopify’s Polaris web components; no arbitrary HTML or CSS | A compiled module (Shopify recommends Rust) and a GraphQL input query |
| Examples | A custom field, an upsell block, a trust banner, a message on the Thank you page | A tiered discount, hiding a delivery or payment option, blocking an order over a limit |
| Placement | Merchant places block targets in the checkout and accounts editor, up to three per location | Activated per store; no visual placement |
| Plan | Information, shipping and payment steps: Plus only. Thank you and Order status: Basic and up | App Store apps: any plan. Custom apps: Plus only |
| Key limits | Only exposed components and APIs; no access to sensitive payment information | 256 kB binary, 11 million instructions and 128 kB input for carts up to 200 lines; up to 25 active per type for most APIs |
Enforcing a rule: use a Function, not the interface
Until recently, a checkout UI extension could stop the buyer from moving to the next step by declaring the block_progress capability and calling useBuyerJourneyIntercept. Shopify deprecated both starting in API version 2026-07 (changelog, July 2, 2026) and says they will be removed in a future version.
The migration path Shopify gives is clear. To enforce a business rule, use a Cart and Checkout Validation Function, which runs server-side and applies the rule consistently across all checkout surfaces, including express wallets and agentic checkout. To reject a discount code with a custom message, use the Discount Function API. The old intercept also depended on the merchant granting the extension that capability, and it ran in the interface; a validation Function is enforced on Shopify’s servers for as long as it is active. See what checkout validation can block.
If an existing app blocks checkout from an extension, plan its move to a validation Function now rather than when the API is removed.
Moving an intercept to a validation Function is usually straightforward, because the rule is the same and only its location changes:
- Write down every condition the intercept checks and the message it shows.
- Confirm the data behind each condition is available in the validation Function’s input query: cart lines, variant, customer or shop metafields, or the validation’s own settings.
- Build the Function to return the same messages, targeted at the relevant field where possible or at the whole cart.
- Keep the extension for anything visual, such as explaining the rule before the buyer hits it, and remove the
block_progresscapability from it.
The plan gates are different
The two tools are gated in different places, which changes what a store below Plus can do:
- UI extensions are gated by where they render. Extensions on the information, shipping and payment steps need Plus. On the Thank you and Order status pages, and on customer account pages, apps with UI extensions work on Basic and higher.
- Functions are gated by how they are distributed. Stores on any plan can use App Store apps that contain Functions; only Plus stores can use custom apps that contain them. A few capabilities, such as payment terms, need Plus either way.
In practice, a non-Plus store can use an App Store app that validates the cart or hides a payment method, but can’t add a custom field to the shipping step from any app.
When you need both
Many requirements combine the two. The extension collects or explains; the Function decides. Typical pairs:
- An extension collects information the buyer must provide; a validation Function blocks checkout when a required value is missing or invalid.
- A Function hides a payment method for certain carts; an extension explains why, so the buyer isn’t left guessing.
- A discount Function applies a tiered price; an extension shows how far the cart is from the next tier.
The rule of thumb: never rely on the interface to enforce anything. If an order must not go through, the Function is what stops it, and the extension is there to make the reason clear.
Common requirements, mapped
| Requirement | What to build |
|---|---|
| Ask for a gift message or delivery instructions | UI extension |
| Show a banner about delivery times at the shipping step | UI extension (Plus, since it is inside a checkout step) |
| Add a survey after purchase | UI extension on the Thank you page (Basic and up) |
| Give 10% off when the cart contains three items from a collection | Discount Function |
| Hide express delivery for oversized items | Delivery Customization Function |
| Hide a payment method for certain carts | Payment Customization Function |
| Refuse orders above a per-product quantity limit | Cart and Checkout Validation Function |
| Show why a payment method is missing | Both: the Function hides it, the extension explains |
| Sell a bundle that ships as separate components | Cart Transform Function |
Two operational limits are worth knowing before you pick. For most Function APIs, including Discount, Delivery Customization, Payment Customization and Cart and Checkout Validation, a store can activate at most 25 functions of each type, across all its apps. On the interface side, a single block location in the checkout editor holds at most three extensions. Stores with many checkout apps can reach either limit.
Functions also run under fixed resource limits: a 256 kB compiled binary, 10,000 kB of linear memory, and, for carts up to 200 lines, 11 million instructions, 128 kB of input and 20 kB of output. A rule that needs large lookup tables has to fit inside those numbers, which is one reason to keep its data in metafields rather than in code.
Choosing, step by step
- Describe the change as a sentence. If it starts with “show”, “ask” or “tell”, it is interface. If it starts with “charge”, “hide”, “reorder” or “block”, it is logic.
- For interface: check which checkout step it belongs on. Information, shipping or payment means Plus.
- For logic: find the Function API (Discount, Delivery Customization, Payment Customization, Cart and Checkout Validation, Cart Transform) and check whether an App Store app already provides it.
- If the logic must be enforced, put it in a Function even when an extension is involved.
- Build and test both on a development store with realistic carts before installing on the live store.
Where Lintel fits
Lintel builds both halves: Shopify Functions for the logic and checkout UI extensions inside a custom app for the interface, scoped to what your plan allows. If an extension of yours still blocks checkout with the deprecated intercept, moving that rule into a validation Function is a well-defined piece of work.
Questions
- Can a checkout UI extension block checkout?
- It could, through useBuyerJourneyIntercept and the block_progress capability, but both are deprecated from API version 2026-07. Shopify recommends a Cart and Checkout Validation Function, which runs server-side.
- Do checkout UI extensions need Shopify Plus?
- On the information, shipping and payment steps, yes. Apps with UI extensions on the Thank you and Order status pages work on the Basic plan and higher.
- Can a Shopify Function add a field to checkout?
- No. Functions have no interface; they return operations such as discounts, hidden options or validation errors. Collecting input is a UI extension’s job.
Sources
- Checkout UI extensions
- Deprecating the useBuyerJourneyIntercept API on checkout UI extensions
- Buyer Journey API
- Function APIs (limitations)
- Cart and Checkout Validation Function API
- Discount Function API
- Delivery Customization Function API
- Payment Customization Function API
- About Shopify Functions (availability)
- Adding apps to your checkout
