Shopify Checkout UI Extensions: What You Can Add, and Where
Checkout UI extension targets for checkout, Thank you and Order status pages, which ones need Shopify Plus, the limits they run under, and when an app is enough.
In this guide
Checkout is the one part of a Shopify store you cannot edit with theme code. If you want a gift message field, a delivery note next to the shipping options, a warning before payment, or a survey on the Thank you page, it has to come from a checkout UI extension: a small piece of UI shipped inside an app and placed by the merchant in the checkout editor.
This guide covers where those extensions can appear today, which placements need Shopify Plus, the limits every extension runs under, and how to decide between an App Store app and one built for you.
If you are here because tracking or custom content disappeared from your Thank you page after the upgrade, start with Thank you page scripts stopped?, which covers pixels and the sunset dates. This post is about the extensions themselves.
What a checkout UI extension is
Shopify’s checkout UI extensions reference describes them as custom UI and logic added to steps of checkout, such as cart review, payment and the Thank you page. In practice:
- The extension lives in an app, public or custom, and is configured in a
shopify.extension.tomlfile. - It is built with Preact and Shopify’s web components (stacks, grids, inputs, buttons and so on), so it looks like the rest of checkout.
- It runs in an isolated sandbox, separate from the checkout page and other extensions. It cannot read the page’s HTML or payment data, and it has limited access to browser APIs.
- It talks to checkout through target APIs: for example
applyCartLinesChange()to add or change cart lines,applyAttributeChange()to save an order attribute,applyDiscountCodeChange()for discount codes, and the Buyer Journey API to control whether the buyer can move to the next step.
That sandbox is the main difference from the old checkout.liquid world. You get a stable set of places and data instead of free access to the page, and in return your extension keeps working when Shopify changes checkout.
Three kinds of target
Every extension declares one or more targets, the places it can render. Shopify groups them into three kinds:
| Kind | Who chooses the position | Example |
|---|---|---|
| Block target | The merchant, in the checkout and accounts editor | purchase.checkout.block.render |
| Static target | Fixed, tied to a checkout feature | purchase.checkout.shipping-option-list.render-after |
| Runnable target | Runs in response to an event and returns data, no UI | purchase.address-autocomplete.suggest |
Static targets only appear when the feature they are attached to is on the page. A target after the pickup location list renders nothing for a buyer who chose shipping.
Checkout targets
From the targets reference, the areas of checkout you can extend are:
| Area | Targets |
|---|---|
| Anywhere the merchant places it | purchase.checkout.block.render |
| Header and footer | purchase.checkout.header.render-after, purchase.checkout.footer.render-after |
| Contact information | purchase.checkout.contact.render-after |
| Delivery address | purchase.checkout.delivery-address.render-before, ...render-after |
| Shipping options | purchase.checkout.shipping-option-list.render-before, ...render-after, purchase.checkout.shipping-option-item.render-after, purchase.checkout.shipping-option-item.details.render |
| Local pickup | purchase.checkout.pickup-location-list.render-before, ...render-after, purchase.checkout.pickup-location-option-item.render-after |
| Payment | purchase.checkout.payment-method-list.render-before, ...render-after |
| Order summary | purchase.checkout.cart-line-item.render-after, purchase.checkout.cart-line-list.render-after, purchase.checkout.reductions.render-before, ...render-after |
| Navigation | purchase.checkout.actions.render-before |
| Address autocomplete | purchase.address-autocomplete.suggest, purchase.address-autocomplete.format-suggestion |
Two more targets, purchase.checkout.pickup-point-list.render-before and ...render-after, are marked in the reference as beta and for Shopify Plus custom apps only.
Thank you and Order status targets
The Thank you page has its own set:
| Area | Target |
|---|---|
| Anywhere the merchant places it | purchase.thank-you.block.render |
| Announcement bar | purchase.thank-you.announcement.render |
| Header and footer | purchase.thank-you.header.render-after, purchase.thank-you.footer.render-after |
| Customer information | purchase.thank-you.customer-information.render-after |
| Order summary | purchase.thank-you.cart-line-item.render-after, purchase.thank-you.cart-line-list.render-after |
The Order status page is now part of customer accounts, so its targets use the customer-account.order-status prefix, such as customer-account.order-status.block.render. We cover those in Shopify customer account extensions.
One timing detail matters for Thank you page extensions. Shopify’s Thank you and Order status guide explains that when a purchase.thank-you extension renders, the order may not be created yet; you get the order ID and confirmation number and can fetch the order later. On the Order status page the order always exists. The same guide states there is no support for APIs that directly change an order from a UI extension.
Which plan you need
This is where most projects are won or lost, so check it before anything else. Shopify’s checkout technologies page and the extension reference say:
| What | Plan |
|---|---|
| Extensions on the information, shipping and payment steps | Shopify Plus only |
| Thank you and Order status page extensions | All plans except Shopify Starter |
| Post-purchase extensions (the upsell page between payment and Thank you) | All plans except Shopify Starter, in beta, access must be requested for live stores |
| Web pixels | All plans except Shopify Starter |
| Checkout branding through the GraphQL Admin API | Shopify Plus |
| Customizing checkout for specific markets | Advanced or Plus, per the help center |
The plan belongs to the store, not the app. A public app that adds a field to the shipping step still only shows it on Plus stores. On Basic, Grow or Advanced, the places you can add UI are the Thank you and Order status pages.
The limits every extension runs under
From the extension reference:
- Bundle size: the compiled extension cannot exceed 64 KB.
- Three per block location: merchants can add up to three extensions to the same block target location.
- Settings: up to 20 merchant-configurable settings per extension.
- No page access: no DOM, no checkout HTML, no payment details.
- Protected customer data: reading buyer details such as name, email or address requires protected customer data access, which goes through Shopify review.
- API versions: each stable version is supported for at least 12 months, and Shopify CLI blocks deploys that target older versions. Plan upgrades the way you would for any app; see our API version upgrade checklist.
Extensions also need to declare capabilities for some behaviour. Shopify’s capabilities guide lists four:
| Capability | Allows |
|---|---|
api_access |
Querying the Storefront API |
network_access |
Calling external servers |
block_progress |
Stopping the buyer from moving on, for example until a required field is filled |
collect_buyer_consent |
Collecting consent for specific policies |
Network access is requested in the app’s API access settings, where Shopify approves it automatically, and then switched on in the TOML. Your server must answer with Access-Control-Allow-Origin: *. Merchants can also disallow block_progress in the checkout editor, so a “required” field built on it must still behave sensibly when blocking is off.
Shopify suggests a simpler route where it fits: write the data your extension needs into metafields ahead of time with the Admin API, and read the metafields in checkout, instead of calling your server on every page load.
[extensions.capabilities]
api_access = true
block_progress = true
[[extensions.targeting]]
target = "purchase.checkout.delivery-address.render-after"
module = "./src/DeliveryNote.jsx"
[[extensions.targeting.metafields]]
namespace = "custom"
key = "delivery_rules"
Practical examples
| Need | Where it goes | Plan |
|---|---|---|
| Gift message or PO number saved on the order | Block or contact target, saved with applyAttributeChange() |
Plus |
| Delivery date picker or note next to shipping options | Shipping option targets | Plus |
| Stop checkout until an age or terms box is ticked | Any checkout target with block_progress |
Plus |
| A message under one product, like “ships separately” | purchase.checkout.cart-line-item.render-after |
Plus |
| Post-purchase survey | purchase.thank-you.block.render |
All except Starter |
| Download links for digital goods | Thank you and Order status blocks | All except Starter |
| Product review prompt after delivery | Order status block | All except Starter |
| Different address suggestions for your market | Address autocomplete targets | Plus |
Shopify’s own guide lists surveys, product reviews, social sharing and download links as typical Thank you and Order status uses. Anything that changes prices, shipping rates or payment methods is not a UI extension job: that is Shopify Functions, often paired with a UI extension that collects the input.
App Store app, Checkout Blocks, or custom extension
Work through these in order.
- Checkout Blocks. Shopify’s Checkout Blocks app covers static content, dynamic content, custom fields, line item content and more, set up in the editor with no code. Per the help center, the static and dynamic content blocks work on Basic and above, and placing them on checkout pages needs Plus. If your need is “show this text when that is true,” start here.
- An App Store app. Upsells, surveys, reviews, loyalty and gift messages are well covered. A maintained public app is usually cheaper than owning code.
- A custom extension. It earns its place when the content depends on your own data or rules: a delivery promise from your warehouse system, a field that must validate against your ERP, a B2B note that depends on the company’s contract, or a mix of placement and logic no app offers.
A custom extension is not a separate product. It ships inside an app, usually a custom app installed on your store, and it follows the distribution rules described in Shopify custom app vs public app. Shopify notes that its checkout design requirements apply to custom apps as well as public ones.
How a custom extension project runs
- Confirm the plan of every store that will use it, and which page each part needs.
- List the data: what the extension reads (cart, buyer, metafields, your server) and what it writes (attributes, cart lines, metafields).
- Request what needs approval early: protected customer data and network access.
- Build and preview with Shopify CLI on a development store.
- Place it in the checkout and accounts editor on a duplicated checkout configuration, test express checkouts and every market, then publish.
- Keep it current with Shopify’s API versions; see maintaining a custom Shopify app.
When to have one built
If Checkout Blocks or an App Store app covers what you need, use it. If the logic is yours, such as rules from your own systems, data that only exists outside Shopify, or a field your operations team depends on, our custom solutions page explains how the team behind Storefleet Store Ops takes on that work and what to send us. For a wider view of the decision, read when to build a custom Shopify app. Other places a custom app can extend Shopify: POS UI extensions for store staff and custom reports with ShopifyQL for the back office.
FAQ
Can I add a custom field to checkout without Shopify Plus?
Not on the information, shipping or payment steps. Those extensions are Plus only, whether the field comes from Checkout Blocks, an App Store app or a custom app. Without Plus, you can add content to the Thank you and Order status pages, or collect the information in the cart on your theme.
Can a checkout UI extension change prices or shipping rates?
No. UI extensions add interface and can change things like cart lines, attributes and discount codes through their APIs. Pricing, shipping rate and payment method logic belongs in Shopify Functions.
Can an extension call my own server?
Yes, with the network_access capability, which you request in the app’s API access settings and declare in the extension’s TOML. Your server must allow requests from any origin. Where possible, Shopify recommends reading data from metafields instead.
How many apps can show content in the same place?
Merchants can add up to three extensions to the same block target location in the checkout and accounts editor.
Does a Thank you page extension see the finished order?
Not always. When a purchase.thank-you extension renders, the order may not be created yet, though the order ID is available. On the Order status page, the order always exists.