A merchant on Shopify Plus asks for gift-wrap at checkout. The requirement sounds simple: let the buyer add a £5 gift-wrap option and include a message for the recipient.
The build requires two components: a UI Extension that renders the gift-wrap checkbox and message field, and a Function that adds the £5 line item when the buyer selects the option. Miss either piece and the checkout either cannot collect the input or cannot apply the charge.
Shopify Plus checkout extensibility splits work into display (UI Extensions) and logic (Functions). Knowing where each lives before scoping prevents spec drift, duplicate effort, and "we thought the other part was included" conversations mid-project.
Introduction
This article teaches you when a Shopify Plus checkout project needs a UI Extension, a Function, or both working together. If your question is whether Plus is the right plan at all, see Choosing the Right Shopify Plan. If checkout abandonment is the immediate pain, start with How to Fix Shopify Checkout Abandonment before adding custom extensions.
The decision merchants face
Shopify Plus unlocks two distinct extension surfaces at checkout:
Checkout UI Extensions render visual elements at defined insertion points: banners, custom fields, product recommendations, trust badges, consent checkboxes, and promotional blocks.
Shopify Functions run backend logic that modifies checkout behaviour: discount calculations, cart line transformations, validation rules, delivery option filtering, and payment method customisation.
The confusion happens because both are "checkout extensions" and both ship via the same app deployment. Merchants often brief a requirement as one thing when the build is two, or scope a Function when the job is purely display.
Checkout extensibility split
Checkout UI Extensions: what they do
UI Extensions are Preact or vanilla JS components that render inside Shopify's hosted checkout. They cannot access the network freely, cannot run arbitrary DOM manipulation, and are sandboxed for security. What they can do:
- Render components at defined targets: after contact fields, around shipping options, in the order summary, in the header or footer, and as merchant-positionable blocks.
- Read checkout state: cart lines, buyer identity, delivery address, selected shipping method, applied discounts.
- Write to checkout state via approved APIs: attributes, metafields, selected options.
- Present inputs: text fields, checkboxes, selects, consent collection.
Common UI Extension jobs
| Job | Extension target | What the Extension does |
|---|---|---|
| Gift message field | purchase.checkout.block.render | Renders a text area; writes to cart attributes |
| Trust badges | purchase.checkout.cart-line-list.render-after | Displays payment icons and guarantee copy |
| Age verification | purchase.checkout.contact.render-after | Presents a checkbox; blocks proceed until checked |
| Delivery instructions | purchase.checkout.delivery-address.render-after | Renders a field for driver notes |
| Upsell banner | purchase.checkout.reductions.render-before | Shows a product recommendation with add button |
| Shipping option explainer | purchase.checkout.shipping-option-item.details.render | Adds detail text under each shipping method |
UI Extensions handle display work: they show information, collect input, and present options. Discount calculations, payment filtering, and cart validation belong in Functions because those jobs change checkout rules rather than render content.
Shopify Functions: what they do
Functions are Rust or JavaScript modules that run on Shopify's backend. They are pure: no network access, no file system, no current time. All data comes via a GraphQL input query; all output is a structured JSON result.
Functions modify checkout behaviour:
- Discount Functions compute product, order, or shipping discounts and return the discount operations to apply.
- Cart Transform Functions expand or modify cart lines: bundle expansion, gift-with-purchase, subscription upgrades.
- Validation Functions accept or reject the cart at checkout, with an error message if rejected.
- Delivery Customisation Functions sort, rename, or hide shipping options based on cart contents or buyer location.
- Payment Customisation Functions sort, rename, or hide payment methods.
- Fulfillment Constraints Functions control how orders route to fulfillment locations.
Common Function jobs
| Job | Function API | What the Function does |
|---|---|---|
| Tiered volume discount | Discount | Returns discount operations when cart total exceeds thresholds |
| Hide COD for high-value orders | Payment Customisation | Removes cash-on-delivery when cart exceeds £500 |
| Block checkout for restricted products | Validation | Rejects cart with an error if product requires age verification |
| Auto-add free gift | Cart Transform | Adds a gift line when cart qualifies |
| Sort shipping by delivery speed | Delivery Customisation | Reorders shipping options fastest-first |
| Regional payment filtering | Payment Customisation | Hides Klarna for buyers outside supported countries |
Functions handle backend logic exclusively, with no rendering capability. When the buyer needs to see why a payment method is hidden or how close they are to a discount threshold, a paired UI Extension handles that display layer.
Criteria that actually matter
Before scoping, answer these questions:
1. Does the job change checkout rules?
If yes, the job needs a Function. Discounts, validation, cart modification, delivery filtering, and payment filtering all live in Functions.
2. Does the job display something to the buyer?
If yes, the job needs a UI Extension. Banners, fields, badges, consent checkboxes, and promotional messages are Extension work.
3. Does the buyer need to see the result of a rule change?
If a Function does something invisible (hide a payment method, auto-apply a discount), and the buyer should understand why, a paired Extension explains it. The Function does the logic; the Extension shows the logic.
4. Does an app already solve this?
Many common jobs (upsells, trust badges, tiered discounts) have App Store solutions that package Extensions and Functions already. Check before building custom. If an app fits, it is faster and cheaper. Custom development is for business-specific logic that apps cannot configure.
| Requirement type | UI Extension | Function | Both |
|---|---|---|---|
| Collect gift message | ✓ | ||
| Add gift-wrap fee | ✓ | ||
| Collect gift message and add fee | ✓ | ||
| Show trust badges | ✓ | ||
| Tiered discount based on cart total | ✓ | ||
| Tiered discount with progress bar | ✓ | ||
| Block checkout for age-restricted items | ✓ | ||
| Age verification with checkbox | ✓ | ||
| Hide payment method for certain products | ✓ | ||
| Explain why payment method is hidden | ✓ | ||
| Delivery date selector | ✓ | ||
| Filter delivery options by postcode | ✓ |
Framework for scoping checkout projects
Use this sequence when a merchant briefs a checkout requirement:
Name the job in buyer terms. What does the buyer do or see differently at checkout?
Split display from logic. If the answer has "show" or "collect", that is Extension work. If the answer has "calculate", "filter", "add", "block", or "sort", that is Function work.
Check whether display depends on logic. If the Extension needs to show the outcome of a rule (discount amount, validation status), the build is paired.
Search the App Store. If an app handles the job, quote the app first. Custom build if the app cannot configure the business-specific requirement.
Map to extension points. For UI work, identify the render target. For Function work, identify the API (Discount, Validation, Cart Transform, etc.).
Scope the data contract. If paired, define how the Extension reads the Function output. Usually via cart attributes, metafields, or checkout state that the Function populates.
Recommendation by scenario
Scenario A: Display only
The job is to show something at checkout without changing checkout behaviour. Examples: trust badges, delivery estimates, brand messaging, field to collect a note.
Build: UI Extension only. Pick the render target closest to where the buyer needs the information. Store collected input in cart attributes or metafields.
Scenario B: Logic only
The job is to change checkout behaviour invisibly. Examples: auto-apply a discount, hide a payment method, block checkout for out-of-stock bundles.
Build: Function only. Define the input query, implement the logic in Rust or JS, return the structured output. No display component.
Scenario C: Logic with display
The job is to change checkout behaviour and communicate the change to the buyer. Examples: tiered discount with progress bar, age verification with checkbox, hidden payment method with explanation.
Build: Function and UI Extension as a paired build. The Function handles logic; the Extension reads checkout state (including Function output where visible) and renders the display. Define the data contract between the two.
Scenario D: App handles it
The job is common enough that an App Store app packages the Extension, Function, or both. Examples: standard upsell modules, Klaviyo checkout fields, ReCharge subscription upgrades.
Build: Configure the app. Reserve custom development for requirements the app cannot meet. Apps are faster to deploy and maintained by the vendor.
Technical constraints worth knowing
Before promising a checkout build, verify:
- Plus is active. Checkout UI Extensions and checkout-specific Functions require Shopify Plus. Standard plans do not have checkout extensibility.
- Target availability. Not all render targets are available on all checkout surfaces. Shop Pay and Thank You pages have different target lists than the main checkout flow.
- Function purity. Functions cannot call external APIs at runtime. All data must come via the input query. If the logic depends on external state (inventory in an ERP, loyalty points in a CRM), that data must sync to Shopify metafields before checkout.
- Component limits. UI Extensions use Shopify's Polaris component library. Custom HTML or third-party JS libraries are not supported. If the design requires unsupported UI, the spec needs adjustment.
- Deployment coupling. Extensions and Functions deploy together as part of a Shopify app. Versioning, testing, and rollback affect both. Plan for coordinated releases if the build is paired.
For a breakdown of how these constraints fit into broader stack architecture, see The Anatomy of a High-Performance Shopify Theme. If the project involves migrating checkout customisations from another platform, migration blueprints cover data and logic porting: Magento to Shopify, WooCommerce to Shopify.
When to get help
Checkout extensibility is mature, but the build surface is not trivial. Consider specialist help when:
- The requirement spans multiple Function APIs and multiple Extension targets.
- The logic depends on external data that needs a sync pipeline before checkout.
- The existing theme or app stack has legacy checkout scripts that need migration.
- The build is paired and the data contract between Function and Extension is unclear.
A structured stack review identifies which checkout jobs are standard, which need custom build, and which need architectural prep before extensibility work starts. See Shopify UI Extensions and Functions services for how we scope these projects, or book a stack assessment to map your checkout roadmap.
Conclusion
Shopify Plus checkout extensibility spans two surfaces: UI Extensions for display and Functions for logic. Most checkout requirements touch one or the other; many touch both.
Before scoping, split the job: what changes checkout rules, and what shows information to the buyer? Match each part to the right extension point. Check the App Store before building custom. If the build is paired, define the data contract between Function and Extension early.
The right split prevents spec drift, keeps builds maintainable, and ensures the buyer sees a coherent checkout experience without invisible logic doing one thing and visible UI doing another.



