If you’ve been running a Shopify Plus store with a customised checkout, 2024 brought one of the biggest required migrations in Shopify’s history. The old checkout.liquid system — which let you drop custom HTML and JavaScript directly into checkout pages — is gone. All Shopify Plus stores must use the new Checkout Extensibility system.
If you’re not on Plus but you’ve wanted checkout customisation, Checkout Extensibility is also now available to standard Shopify plans with a limited feature set.
This guide explains what changed, what you can actually customise now, what you can’t, and when you need a developer to make it work.
What Was checkout.liquid?
checkout.liquid was a Liquid template file that Shopify Plus merchants could edit directly. Like any Liquid template, you could add custom HTML, include external scripts, display custom fields, restructure the layout, and integrate third-party services.
It was powerful because it treated the checkout like any other page on your Shopify theme — editable HTML with access to Liquid variables.
It was also dangerous for the same reason. Custom JavaScript in checkout caused:
- Broken checkout experiences when Shopify updated its checkout logic
- Security vulnerabilities (payment page scripts are a top target for card skimmers)
- Performance degradation from poorly-optimised third-party scripts
- Maintenance burden after every Shopify update
Shopify deprecated checkout.liquid and required migration to Checkout Extensibility for all Shopify Plus stores by August 2024.
Official reference: Checkout Extensibility overview
What Is Checkout Extensibility?
Checkout Extensibility is Shopify’s new system for customising the checkout experience. Instead of editing a template directly, you extend checkout through two mechanisms:
- UI Extensions — inject custom UI elements (fields, banners, content blocks) into specific slots in the checkout layout
- Shopify Functions — modify checkout business logic (discounts, shipping, payment, cart validation) via server-side serverless functions
The key philosophical difference: you’re extending a Shopify-controlled checkout, not editing a template. Shopify controls the layout, security, and performance of the checkout itself. Your customisations plug into designated extension points.
Part 1: Checkout UI Extensions
UI Extensions let you add custom UI elements to specific places in the checkout. They’re built as React components using Shopify’s @shopify/ui-extensions-react library.
Where You Can Add UI Elements
Shopify defines specific extension targets — slots where you can inject content. Key targets:
| Target | What You Can Do |
|---|---|
purchase.checkout.block.render | General-purpose block anywhere in checkout |
purchase.checkout.delivery-address.render-before | Content before shipping address form |
purchase.checkout.shipping-option-list.render-after | Content after shipping options |
purchase.checkout.payment-method-list.render-before | Content before payment methods |
purchase.checkout.order-summary.cart-line-item.render-after | Upsell/cross-sell below cart item |
purchase.checkout.footer.render-before | Footer area content |
purchase.thank-you.block.render | Thank you page blocks |
purchase.order-status.block.render | Order status page blocks |
Limitations of UI Extension targets:
- You can only add content to these predefined slots — you cannot reorder existing checkout elements
- You cannot remove Shopify’s native checkout fields
- You cannot inject arbitrary JavaScript that affects the checkout payment flow
Building a UI Extension
You build UI extensions inside a Shopify app (public or custom). Here’s a minimal example:
# Create a new Shopify app with the CLI
npm create @shopify/app@latest
cd my-checkout-app
shopify app generate extension
# Select: Checkout UI extension
This generates:
extensions/
my-extension/
src/
Checkout.tsx ← your React component
shopify.extension.toml
A basic extension that adds a custom field to checkout:
// extensions/gift-message/src/Checkout.tsx
import {
reactExtension,
BlockStack,
Text,
TextField,
useApplyAttributeChange,
useAttributes,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => <GiftMessageExtension />,
);
function GiftMessageExtension() {
const applyAttributeChange = useApplyAttributeChange();
const attributes = useAttributes();
const currentMessage = attributes.find(
(attr) => attr.key === 'gift_message'
)?.value || '';
return (
<BlockStack spacing="base">
<Text size="base" emphasis="bold">
Add a gift message
</Text>
<TextField
label="Gift message (optional)"
value={currentMessage}
onChange={(value) => {
applyAttributeChange({
type: 'updateAttribute',
key: 'gift_message',
value,
});
}}
multiline={3}
maxLength={200}
/>
</BlockStack>
);
}
What the APIs give you:
useApplyAttributeChange— add order attributes (stored on the order in Shopify)useCartLines— read cart itemsuseShippingAddress— read shipping addressuseCustomer— read customer datauseApplyDiscountCodeChange— apply/remove discount codesuseExtensionLanguage— localisation
What you can’t do in UI Extensions:
- Run arbitrary server-side logic
- Access
documentorwindow(no raw DOM manipulation) - Load external scripts from third-party domains
- Redirect the user away from checkout
Part 2: Shopify Functions
Shopify Functions are serverless functions that run server-side to modify checkout business logic. Unlike UI Extensions which control what users see, Functions control what happens.
What Functions Can Do
| Function Type | Use Case |
|---|---|
| Discount | Custom discount logic (e.g., buy 3 pay for 2, tiered discounts based on customer tags) |
| Cart and Checkout Validation | Block checkout if conditions aren’t met (e.g., minimum order, product combination restrictions) |
| Payment Customisation | Hide or reorder payment methods based on cart contents, customer location, or order value |
| Shipping Customisation | Hide, rename, or reorder shipping options based on cart data |
| Order Routing | Route orders to different fulfilment locations based on rules |
| Fulfillment Constraints | Define which locations can fulfil which items |
Writing a Shopify Function
Functions are written in Rust (for performance) or JavaScript/TypeScript. The Shopify CLI handles compilation. Here’s a JavaScript discount function example:
shopify app generate extension
# Select: Discount Function (JavaScript)
Generated structure:
extensions/
volume-discount/
src/
run.js ← your function logic
shopify.extension.toml
input.graphql ← what cart data you need
schema.graphql ← Shopify-generated, don't edit
A volume discount function (buy 3+ get 10% off):
// extensions/volume-discount/src/run.js
export function run(input) {
const targets = [];
const discountLines = [];
for (const line of input.cart.lines) {
if (line.quantity >= 3) {
targets.push({
cartLine: {
id: line.id,
},
});
discountLines.push({
value: { percentage: { value: 10 } },
targets: [{ cartLine: { id: line.id } }],
});
}
}
if (discountLines.length === 0) {
return { discounts: [], discountApplicationStrategy: 'FIRST' };
}
return {
discounts: discountLines,
discountApplicationStrategy: 'FIRST',
};
}
The input.graphql file defines what cart data your function receives:
# input.graphql — request only what you need
query RunInput {
cart {
lines {
id
quantity
merchandise {
... on ProductVariant {
id
product {
id
tags
}
}
}
}
}
}
Reference: Shopify Functions API documentation
What You Can Customise With Checkout Extensibility
✅ You CAN:
Add custom fields and information:
- Gift message input field (stored as order attribute)
- Custom date picker (e.g., delivery date selection)
- Custom checkbox (e.g., “I agree to special installation terms”)
- Business tax ID or VAT number field for B2B
- Tip/gratuity selector
Add information banners:
- “Orders placed before 2pm ship same day”
- Trust badges (BBB, secure checkout icons)
- Free shipping progress indicator
- Custom returns policy text
Add upsells and cross-sells:
- Product upsell below cart line items
- “Add gift wrapping for £3” toggle
- Extended warranty selector
Modify checkout business logic:
- Tiered discounts (Functions)
- Minimum order validation (Functions)
- Hide incompatible shipping methods (Functions)
- Hide pay-later options for restricted products (Functions)
- Custom B2B payment terms (Functions)
Customise the thank-you page:
- Add post-purchase survey
- Display loyalty points earned
- Show referral program CTA
- Video or custom confirmation content
❌ You CANNOT:
Structurally redesign the checkout:
- Reorder the address → shipping → payment steps
- Add a custom pre-checkout step
- Create a multi-page custom checkout flow
- Remove the Shopify checkout header/footer
Inject arbitrary JavaScript:
- No
<script>tags with external code - No direct DOM manipulation
- No custom CSS that overrides Shopify’s checkout styles
Remove native Shopify fields:
- Cannot hide the email field
- Cannot remove the billing address section
- Cannot modify Shopify’s native validation
Access full checkout performance data directly:
- GA4 checkout events must come through Shopify’s
web_pixelsAPI, not injected scripts
GA4 and GTM in the New Checkout
This is where most merchants hit a wall. The old checkout.liquid approach of injecting gtag.js directly is no longer possible.
Web Pixels API (Shopify’s Analytics Solution)
Shopify provides the Web Pixels API as the official way to add analytics to checkout. A web pixel is a sandboxed JavaScript snippet that runs in a separate iframe with access to Shopify checkout events.
Key checkout events available via web pixels:
checkout_started— began checkoutcheckout_address_info_submitted— completed address stepcheckout_shipping_info_submitted— selected shippingcheckout_completed— purchase confirmedpayment_info_submitted— entered payment info
Setting up GA4 via Web Pixels:
- In Shopify Admin → Settings → Customer events → Add custom pixel
- Pixel type: Custom
- Write pixel code:
// Custom Web Pixel for GA4
analytics.subscribe('checkout_completed', (event) => {
const checkout = event.data.checkout;
// Send purchase event to GA4
gtag('event', 'purchase', {
transaction_id: checkout.order.id,
value: parseFloat(checkout.totalPrice.amount),
currency: checkout.currencyCode,
items: checkout.lineItems.map((item, index) => ({
item_id: item.variant.sku || item.variant.id,
item_name: item.title,
price: parseFloat(item.variant.price.amount),
quantity: item.quantity,
index: index,
})),
});
});
Limitation: Web pixels run in a sandboxed iframe with restricted network access. They can make requests to declared endpoints but can’t load arbitrary external scripts in real-time. You must declare which domains your pixel communicates with.
Reference: Web Pixels API documentation
GTM in Shopify Checkout (The Current Reality)
GTM cannot be injected into Shopify’s checkout the way it used to be via checkout.liquid. Your options:
- Use Web Pixels for checkout events — implement GA4 purchase events directly via the Web Pixels API (shown above)
- Use Shopify’s native GA4 integration — Shopify’s built-in Google & YouTube channel handles basic purchase tracking automatically
- Use server-side tracking — the Conversions API approach where your server sends purchase events after order creation (most reliable)
For e-commerce stores where checkout conversion data is critical, server-side tracking via the Orders API or webhooks is the most reliable approach — a webhook fires on orders/paid, your server extracts the order data, and sends a purchase event to GA4 via the Measurement Protocol.
Branding the New Checkout
While structural customisation is limited, Shopify does let you brand the checkout appearance:
Available in Shopify Admin → Settings → Checkout → Branding:
- Logo (upload a custom logo for the checkout header)
- Background colour and image
- Button colours
- Font selection (from a limited set)
- Input field styling (corner radius, border style)
- Accent colours
Checkout Editor (Shopify Plus only): Shopify Plus merchants get access to the Checkout Editor — a drag-and-drop tool that lets you add approved app extensions to the checkout without code.
- Shopify Admin → Online Store → Themes → Customize → dropdown → Checkout
- The Checkout Editor shows you approved extension slots
- Add installed app extensions by clicking the
+in each slot
Migration Checklist: checkout.liquid → Checkout Extensibility
If you’re migrating from checkout.liquid, audit your current customisations:
□ List all custom fields in checkout.liquid → rebuild as UI Extensions
□ List all custom scripts → review which are replaceable via Web Pixels
□ List all discount logic → rebuild as Discount Functions
□ List all shipping rule customisations → rebuild as Shipping Functions
□ Payment method customisations → rebuild as Payment Functions
□ Analytics/GTM integration → rebuild via Web Pixels or server-side
□ Third-party app scripts → check if app has a UI Extension equivalent
□ Custom CSS overrides → rebuild via Checkout Branding API where possible
□ Test each extension in a development theme before going live
Reference: checkout.liquid migration guide
When Do You Need a Developer?
You can DIY:
- Branding changes (logo, colours, fonts) — all in Shopify Admin, no code
- Basic trust badge or banner — using an app from the App Store with a UI Extension
- Gift message field — several free apps provide this as a UI Extension
- Discount codes — native Shopify discount system handles most cases
You need a developer for:
- Custom UI Extensions with specific data — if you need to show inventory status, customer-specific pricing, or data from an external system in checkout
- Custom Shopify Functions — tiered discounts, complex validation rules, payment method rules based on multiple conditions
- Server-side analytics setup — Measurement Protocol purchase tracking via webhooks
- B2B checkout customisation — net terms, PO number fields, tax exemption handling
- Custom upsell logic — product recommendations based on cart contents with live pricing
- Multi-language/multi-currency edge cases — customisations that need to behave differently across markets
The Bottom Line for Shopify Merchants
Checkout Extensibility is more restrictive than checkout.liquid for power users, but it’s a better system overall. The sandboxed approach means fewer checkout-breaking deployments, better security (critical for a payment page), and consistent performance.
What most merchants actually need — gift message, trust badges, shipping messaging, basic upsells — is achievable with apps that have already built UI Extensions. Check the Shopify App Store first before commissioning custom development.
What requires custom development: B2B-specific customisations, complex discount logic, custom analytics setups, and anything that requires data from your own systems inside the checkout flow.
We build Shopify UI Extensions, Functions, and checkout analytics integrations for merchants who’ve outgrown what App Store solutions provide. Book a free consultation to scope your checkout project.