Shopify Checkout Extensibility: The Developer Guide for 2026
Checkout.liquid is officially unsupported for the information, shipping, and payment checkout steps. Shopify deprecated it in August 2024, and the Thank You and Order Status pages follow by August 2026. If you are still running checkout customizations on checkout.liquid, you are on borrowed time.
Checkout Extensibility is the replacement, and it is fundamentally different in architecture. Instead of editing a monolithic Liquid template where you could inject arbitrary HTML, CSS, and JavaScript, you now build sandboxed extensions using TypeScript and React components that Shopify controls, validates, and renders inside a secure checkout environment.
As a Shopify Partner that has migrated multiple Plus stores from checkout.liquid to Extensibility, we have learned what the documentation does not tell you. This guide covers the real developer experience — the architecture, the code, the gotchas, and the migration path.
The Two Layers: Checkout UI Extensions and Shopify Functions
Checkout Extensibility is not one thing. It is two distinct systems that do different jobs:
Checkout UI Extensions — the frontend layer. These render custom UI components at defined points in the checkout flow. Think: informational banners, custom fields, upsell blocks, loyalty program displays, gift message inputs. They run in a sandboxed iframe-like environment with no access to the real DOM.
Shopify Functions — the backend layer. These execute custom business logic server-side during checkout. Think: custom discount rules, payment method filtering, shipping rate customization, cart validation. They run in WebAssembly (Wasm) for performance and security.
Most checkout customizations require one or both. A custom upsell at checkout needs a UI Extension to display the offer and a Shopify Function to apply the discount when the customer accepts.
Checkout UI Extensions: Architecture Deep Dive
The Sandbox Model
UI Extensions run in isolation. They cannot:
- Access the checkout DOM
- Inject arbitrary HTML or
<script>tags - Read or modify payment information
- Override the merchant's checkout branding (fonts, colors)
- Use browser APIs like
window,document, orlocalStorage
This is deliberate. Shopify's checkout handles billions of dollars in transactions. The sandbox ensures that no extension can compromise checkout security, break other extensions, or degrade performance.
What they CAN do:
- Render Shopify's pre-built UI components (Banner, BlockStack, Checkbox, TextField, etc.)
- Read cart contents, customer information (with permission), and shipping details
- Write metafield data to the order
- Communicate with your app's backend via
fetch(within the extension context) - React to checkout events (shipping address changes, payment method selection)
Render Targets: Where Extensions Live
Every UI Extension declares a render target — the specific location in checkout where it appears. Shopify organizes these into three levels:
Page-level targets — which checkout step the extension appears on:
- Information page (email, shipping address)
- Shipping page (shipping method selection)
- Payment page (payment method, billing address)
- Thank You page
- Order Status page
Area-level targets — which section within a page:
- Header
- Main content area
- Order summary
- Footer
Specific placement targets — exact positions relative to checkout elements:
purchase.checkout.cart-line-item.render-after— after each cart line itempurchase.checkout.shipping-option-list.render-before— before shipping optionspurchase.checkout.payment-method-list.render-after— after payment methods- And dozens more
There are two types of placements:
Static extensions — locked to a core checkout feature. They render immediately before or after a specific element (like contact information or shipping methods). Best when your functionality directly relates to that checkout feature.
Block extensions — freely placeable between core features. Merchants drag and drop these in the Checkout Editor. Best when your functionality is independent of any specific checkout element.
Building Your First Extension
Here is the complete setup for a Checkout UI Extension that adds a custom gift message field:
1. Generate the extension:
# Inside your Shopify app directory
shopify app generate extension
# Select: Checkout UI Extension
# Name: gift-message
# Language: TypeScript React
This creates the extension directory structure:
extensions/
gift-message/
src/
Checkout.tsx # Your extension code
shopify.extension.toml # Configuration
package.json
2. Configure the extension (shopify.extension.toml):
api_version = "2026-01"
[[extensions]]
type = "ui_extension"
name = "Gift Message"
handle = "gift-message"
[[extensions.targeting]]
module = "./src/Checkout.tsx"
target = "purchase.checkout.block.render"
3. Write the extension (Checkout.tsx):
import {
reactExtension,
Banner,
BlockStack,
Checkbox,
TextField,
useMetafieldContext,
useApplyMetafieldsChange,
useExtensionCapability,
useBuyerJourneyIntercept,
} from '@shopify/ui-extensions-react/checkout';
import { useState } from 'react';
export default reactExtension(
'purchase.checkout.block.render',
() => <GiftMessage />
);
function GiftMessage() {
const [isGift, setIsGift] = useState(false);
const [message, setMessage] = useState('');
const applyMetafieldsChange = useApplyMetafieldsChange();
const canBlockProgress = useExtensionCapability('block_progress');
// Save the gift message as an order metafield
const handleMessageChange = (value: string) => {
setMessage(value);
applyMetafieldsChange({
type: 'updateMetafield',
namespace: 'custom',
key: 'gift_message',
valueType: 'string',
value: value,
});
};
// Optional: block checkout progress if gift is selected but no message
useBuyerJourneyIntercept(({ canBlockProgress }) => {
if (canBlockProgress && isGift && message.trim() === '') {
return {
behavior: 'block',
reason: 'Gift message is required when sending as a gift',
errors: [
{
message: 'Please enter a gift message',
target: '$.cart.attribute.gift_message',
},
],
};
}
return { behavior: 'allow' };
});
return (
<BlockStack spacing="base">
<Checkbox
checked={isGift}
onChange={setIsGift}
>
This order is a gift
</Checkbox>
{isGift && (
<TextField
label="Gift message"
value={message}
onChange={handleMessageChange}
multiline={3}
maxLength={200}
/>
)}
{isGift && message && (
<Banner status="info">
Your gift message will be included with the order.
</Banner>
)}
</BlockStack>
);
}
4. Test locally:
shopify app dev
This starts a local development server. Navigate to your store's checkout to see the extension rendered in real-time. The Checkout Editor (in Shopify admin) lets the merchant position block extensions where they want them.
Available UI Components
Shopify provides a component library specifically for checkout extensions. You cannot use arbitrary HTML or your own component library. The available components include:
Layout: BlockStack, InlineStack, Grid, View, ScrollView, Divider
Content: Text, TextBlock, Heading, Image, Icon, Badge, Banner, Tag, SkeletonText, SkeletonImage
Forms: TextField, Checkbox, ChoiceList, Select, DatePicker, PhoneField, Stepper, ToggleButton, ToggleButtonGroup
Interactive: Button, Pressable, Link, Disclosure, Modal, Popover, Sheet, Tooltip
Data: List, ResourceItem, ProductThumbnail, Money
These components automatically inherit the merchant's checkout branding (colors, fonts, corner radius). You cannot override the CSS. This is intentional — it ensures a consistent checkout experience regardless of how many extensions are installed.
Shopify Functions: Backend Logic
Shopify Functions run server-side during checkout, executing custom business logic. They compile to WebAssembly for near-native performance and strict sandboxing.
Function Types
Discount Functions — create custom discount rules beyond what Shopify's built-in discounts support:
- Product discounts (custom BOGO logic, tiered pricing)
- Order discounts (conditional free shipping, loyalty discounts)
Delivery Functions — customize shipping options:
- Delivery customization (rename, reorder, hide shipping methods)
- Custom shipping rate calculation
Payment Functions — control payment method availability:
- Hide specific payment methods based on cart contents, customer tags, or address
- Reorder payment method display
Cart Functions — validate and transform the cart:
- Cart transform (bundle expansion, automatic add-ons)
- Validation (enforce business rules at checkout)
A Simple Shopify Function: Payment Method Filtering
This function hides the "Cash on Delivery" payment method for orders over a certain value:
1. Generate the function:
shopify app generate extension
# Select: Function
# Type: Payment customization
# Name: hide-cod-high-value
# Language: TypeScript
2. Define the input query (input.graphql):
query Input {
cart {
cost {
totalAmount {
amount
currencyCode
}
}
}
paymentMethods {
id
name
}
}
3. Write the function (src/run.ts):
import type {
RunInput,
FunctionRunResult,
} from '../generated/api';
const COD_THRESHOLD = 500; // Hide COD above this amount
export function run(input: RunInput): FunctionRunResult {
const cartTotal = parseFloat(input.cart.cost.totalAmount.amount);
// If cart total is under threshold, allow all payment methods
if (cartTotal <= COD_THRESHOLD) {
return { operations: [] };
}
// Find COD payment method and hide it
const codMethod = input.paymentMethods.find(
(method) => method.name.toLowerCase().includes('cash on delivery')
);
if (!codMethod) {
return { operations: [] };
}
return {
operations: [
{
hide: {
paymentMethodId: codMethod.id,
},
},
],
};
}
This function runs in WebAssembly on Shopify's servers. It executes in microseconds and has zero impact on checkout page load time. The tradeoff: you cannot make external API calls from within a Function. All logic must be self-contained using the input data Shopify provides.
The Migration Path from checkout.liquid
If your store currently uses checkout.liquid customizations, here is the migration playbook:
Step 1: Audit Your Current Customizations
Shopify provides a migration report in your admin: Settings → Checkout → Checkout Extensibility shows a summary of your current checkout.liquid customizations with guidance on replacement options.
Common customizations and their Extensibility equivalents:
| checkout.liquid Pattern | Extensibility Replacement |
|---|---|
| Custom CSS styling | Checkout Branding API |
| Additional form fields | Checkout UI Extension (TextField, Checkbox) |
| Upsell/cross-sell offers | Checkout UI Extension (product offer block) |
| Trust badges/security seals | Checkout UI Extension (Image + Banner) |
| Custom order notes | Checkout UI Extension (TextField + metafields) |
| Conditional payment hiding | Shopify Function (Payment customization) |
| Custom discount logic | Shopify Function (Discount) |
| Custom shipping rules | Shopify Function (Delivery customization) |
| Order validation | Shopify Function (Cart validation) |
| Post-purchase surveys | Post-Purchase Extension |
| Tracking scripts/pixels | Web Pixel Extension |
Step 2: Prioritize by Impact
Not all customizations need to migrate at once. Prioritize:
- Revenue-impacting customizations first (upsells, discount logic)
- Compliance requirements next (tax displays, required disclosures)
- Nice-to-have UX features last (trust badges, custom messaging)
Step 3: Build and Test in Development
Shopify allows you to run checkout.liquid and Extensibility in parallel during migration. Build your extensions on a development store, test thoroughly, then upgrade your production checkout.
Critical: Once you upgrade to Checkout Extensibility, ALL checkout.liquid customizations are permanently removed. There is no rollback. Back up your checkout.liquid code before upgrading.
Step 4: Upgrade in Shopify Admin
Settings → Checkout → Customize checkout → Enable Checkout Extensibility. After this, use the Checkout Editor to arrange your extensions.
Shopify Scripts vs. Shopify Functions
Shopify Scripts (the older customization system using Ruby) will continue working alongside Checkout Extensibility until June 30, 2026. After that date, Scripts are sunset.
If you have Shopify Scripts running:
- Line item scripts → migrate to Product Discount Functions
- Shipping scripts → migrate to Delivery Customization Functions
- Payment scripts → migrate to Payment Customization Functions
Start migrating Scripts now. The June 2026 deadline is firm.
The Checkout Editor: Merchant-Facing Configuration
After building and deploying your extensions, merchants configure them via the Checkout Editor:
Settings → Checkout → Customize opens a visual editor similar to the theme customizer. Merchants can:
- Enable/disable installed extensions
- Drag block extensions to their preferred positions
- Configure extension settings (if you exposed them via
settingsin the extension config) - Preview the checkout with extensions active
This is a significant improvement over checkout.liquid, where merchants had zero control over customization placement without developer intervention.
Performance Considerations
Checkout Extensibility is designed for performance, but you can still create slow extensions:
Keep UI Extensions lightweight. Every extension adds to the checkout's JavaScript bundle. Avoid heavy computations in render functions. Use Shopify's built-in hooks instead of reimplementing logic.
Minimize API calls from extensions. If your extension needs backend data, fetch it once on load and cache the result. Do not make API calls on every re-render.
Shopify Functions are fast by design. They run in WebAssembly with strict resource limits. If your function exceeds time or memory limits, it fails silently and the default behavior applies. Keep function logic simple and deterministic.
Test on mobile. Checkout performance matters most on mobile, where connections are slower and devices are less powerful. Test every extension on real mobile devices, not just browser dev tools.
We reduced cart abandonment by 22% for Baby Forest's launch partly by keeping the checkout fast and focused — minimal extensions, maximum performance.
Real-World Extension Patterns
Based on our builds, here are the most common and valuable extension patterns:
Delivery date picker — UI Extension on the shipping step. Lets customers choose a preferred delivery date. Saves the selection as an order metafield. Pairs well with fulfillment workflow automation.
B2B purchase order field — UI Extension on the payment step for B2B checkout flows. Captures the PO number and associates it with the order.
Free gift threshold banner — UI Extension in the order summary. Shows how much more the customer needs to spend to unlock a free gift. Uses cart cost data from the extension API.
COD restriction by region — Shopify Function (Payment customization). Hides cash on delivery for specific shipping zones where COD is not operationally viable. Critical for GCC ecommerce where COD availability varies by emirate.
Loyalty points display — UI Extension showing how many loyalty points this order earns. Fetches data from your loyalty backend via the extension's network capabilities.
Deployment and Version Management
Extensions deploy through your Shopify app:
# Deploy all extensions
shopify app deploy
# This builds, validates, and publishes your extensions
# Merchants see updates immediately after deployment
Version management: Shopify Extensions are tied to your app version. When you deploy a new app version, all extensions update simultaneously. There is no way to deploy individual extensions independently — plan your release cycles accordingly.
API versioning: Extensions declare an api_version in their TOML configuration. Shopify releases new API versions quarterly (2025-07, 2025-10, 2026-01, 2026-04). You should update your api_version at least annually to access new components and APIs.
Frequently Asked Questions
Written by

Founder & CEO
Rishabh Sethia is the founder and CEO of Innovatrix Infotech, a Kolkata-based digital engineering agency. He leads a team that delivers web development, mobile apps, Shopify stores, and AI automation for startups and SMBs across India and beyond.
Connect on LinkedIn