Shopify deprecated checkout.liquid in August 2024. If you’re still using it, you’re on borrowed time. Checkout Extensibility is the new way to customize Shopify checkout—and honestly, it’s better. Here’s everything you need to know to migrate or build new checkout customizations.
What Changed (And Why It Matters)
Before Checkout Extensibility, customizing checkout required:
- Shopify Plus for checkout.liquid access
- Direct Liquid/CSS/JS edits to the checkout template
- Careful testing (breaking checkout = losing sales)
- Manual maintenance with every Shopify update
The problems with checkout.liquid:
- Security risks (direct code injection)
- Performance issues (unoptimized custom code)
- Upgrade friction (custom code broke with updates)
- Limited functionality (no backend logic)
Checkout Extensibility solves all of these:
- Sandboxed extensions (can’t break checkout)
- Optimized runtime (Shopify controls performance)
- Upgrade-safe (extensions adapt to checkout changes)
- Full functionality (UI extensions + backend logic)
What You Can Build
Checkout Extensibility supports three types of customizations:
1. Checkout UI Extensions
Add custom UI elements to checkout:
- Custom input fields (gift messages, delivery instructions)
- Upsells and cross-sells
- Trust badges and guarantees
- Loyalty point displays
- Custom shipping options
- Order notes and preferences
2. Shopify Functions
Backend logic that runs during checkout:
- Custom discounts (complex rules, tiered pricing)
- Shipping rate customization (rules-based shipping)
- Payment method customization (show/hide based on conditions)
- Cart validation (quantity limits, product restrictions)
3. Web Pixels
Tracking and analytics:
- Custom analytics events
- Conversion tracking
- Marketing pixels
- Customer behavior tracking
Getting Started
Prerequisites
You need:
- Shopify CLI installed (
npm install -g @shopify/cli) - Node.js 18+
- A Shopify Partner account
- A development store (or Plus store for production)
Note: Checkout UI Extensions are available on ALL Shopify plans (not just Plus). This is a huge change—you no longer need Plus to customize checkout appearance.
Create Your First Extension
# Create a new app
shopify app init
# Navigate to your app
cd your-app-name
# Generate a checkout UI extension
shopify app generate extension --type checkout_ui_extension
This creates a scaffold with:
extensions/
└── checkout-ui/
├── src/
│ └── Checkout.jsx
├── locales/
│ └── en.default.json
└── shopify.extension.toml
Basic Extension Example
Here’s a simple “Gift Message” field extension:
// extensions/checkout-ui/src/Checkout.jsx
import {
extension,
BlockStack,
TextField,
useMetafield,
useApplyMetafieldsChange,
} from '@shopify/ui-extensions-react/checkout';
export default extension('purchase.checkout.block.render', (root, api) => {
return <GiftMessage />;
});
function GiftMessage() {
const giftMessage = useMetafield({
namespace: 'custom',
key: 'gift_message',
});
const applyMetafieldsChange = useApplyMetafieldsChange();
const handleChange = (value) => {
applyMetafieldsChange({
type: 'updateMetafield',
namespace: 'custom',
key: 'gift_message',
valueType: 'string',
value,
});
};
return (
<BlockStack>
<TextField
label="Gift message (optional)"
value={giftMessage?.value || ''}
onChange={handleChange}
maxLength={200}
/>
</BlockStack>
);
}
Deploy and Test
# Start development server
shopify app dev
# Deploy to production
shopify app deploy
Real-World Use Cases
Use Case 1: Checkout Upsells
Add post-add-to-cart upsells in checkout:
import {
extension,
BlockStack,
InlineStack,
Image,
Text,
Button,
useCartLines,
useApplyCartLinesChange,
} from '@shopify/ui-extensions-react/checkout';
function CheckoutUpsell() {
const cartLines = useCartLines();
const applyCartLinesChange = useApplyCartLinesChange();
// Show upsell if cart value is under $100
const cartTotal = cartLines.reduce((sum, line) =>
sum + (line.cost.totalAmount.amount * 1), 0
);
if (cartTotal >= 100) return null;
const handleAddUpsell = async () => {
await applyCartLinesChange({
type: 'addCartLine',
merchandiseId: 'gid://shopify/ProductVariant/12345',
quantity: 1,
});
};
return (
<BlockStack border="base" padding="base" borderRadius="base">
<Text emphasis="bold">Add $20 more for free shipping!</Text>
<InlineStack>
<Image source="product-image.jpg" />
<BlockStack>
<Text>Recommended Product</Text>
<Text appearance="subdued">$25.00</Text>
<Button onPress={handleAddUpsell}>Add to Order</Button>
</BlockStack>
</InlineStack>
</BlockStack>
);
}
Use Case 2: Delivery Date Picker
Let customers choose delivery date:
import {
extension,
DatePicker,
BlockStack,
Text,
useMetafield,
useApplyMetafieldsChange,
} from '@shopify/ui-extensions-react/checkout';
function DeliveryDatePicker() {
const deliveryDate = useMetafield({
namespace: 'custom',
key: 'delivery_date',
});
const applyChange = useApplyMetafieldsChange();
// Calculate min date (3 days from now)
const minDate = new Date();
minDate.setDate(minDate.getDate() + 3);
// Max date (30 days from now)
const maxDate = new Date();
maxDate.setDate(maxDate.getDate() + 30);
return (
<BlockStack>
<Text emphasis="bold">Choose Delivery Date</Text>
<DatePicker
selected={deliveryDate?.value}
onChange={(value) => {
applyChange({
type: 'updateMetafield',
namespace: 'custom',
key: 'delivery_date',
valueType: 'string',
value,
});
}}
disabled={[
{ start: '2000-01-01', end: minDate.toISOString().split('T')[0] },
]}
/>
</BlockStack>
);
}
Use Case 3: Custom Discount with Shopify Functions
Create a “Buy 2, Get 1 Free” discount:
// extensions/product-discount/src/run.js
export function run(input) {
const discounts = [];
// Group line items by product
const productGroups = {};
for (const line of input.cart.lines) {
const productId = line.merchandise.product.id;
if (!productGroups[productId]) {
productGroups[productId] = [];
}
productGroups[productId].push(line);
}
// Apply "Buy 2 Get 1 Free" logic
for (const [productId, lines] of Object.entries(productGroups)) {
const totalQuantity = lines.reduce((sum, line) => sum + line.quantity, 0);
const freeItems = Math.floor(totalQuantity / 3);
if (freeItems > 0) {
// Find the lowest priced variant to discount
const sortedLines = [...lines].sort(
(a, b) => a.cost.amountPerQuantity.amount - b.cost.amountPerQuantity.amount
);
let remainingFree = freeItems;
for (const line of sortedLines) {
if (remainingFree <= 0) break;
const discountQuantity = Math.min(remainingFree, line.quantity);
remainingFree -= discountQuantity;
discounts.push({
targets: [{ cartLine: { id: line.id } }],
value: {
percentage: { value: 100 },
},
message: 'Buy 2, Get 1 Free!',
});
}
}
}
return { discounts };
}
Extension Placement Targets
Checkout Extensibility defines specific locations where you can add UI:
| Target | Location | Use Case |
|---|---|---|
purchase.checkout.header.render-after | After header | Announcements, promos |
purchase.checkout.block.render | Main content | Custom fields, upsells |
purchase.checkout.contact.render-after | After contact info | Additional contact fields |
purchase.checkout.shipping-address.render-after | After shipping | Delivery preferences |
purchase.checkout.payment-method.render-after | After payment | Payment-related info |
purchase.checkout.footer.render-before | Before footer | Trust badges, guarantees |
Migration from checkout.liquid
If you’re currently using checkout.liquid, here’s the migration path:
Step 1: Audit Current Customizations
List everything you’ve customized:
- Custom CSS styling
- Additional form fields
- Custom JavaScript
- Third-party scripts
- Analytics tracking
Step 2: Map to Extensibility Features
| checkout.liquid Feature | Extensibility Equivalent |
|---|---|
| Custom CSS | UI Extension components (styled) |
| Custom form fields | UI Extension with TextField/Select |
| Custom JS logic | Shopify Functions |
| Analytics scripts | Web Pixels |
| Upsell widgets | UI Extension with cart modification |
Step 3: Build and Test
- Create extensions for each customization
- Test thoroughly in development store
- Deploy to staging/preview
- Gradual rollout to production
Timeline
Shopify’s deadline for checkout.liquid deprecation was August 2024. If you haven’t migrated:
- Contact Shopify support about your migration timeline
- Prioritize critical customizations first
- Consider hiring a Shopify expert if needed
Performance Considerations
Checkout Extensibility is designed for performance, but you can still impact it:
Do’s
- Keep extensions lightweight
- Use Shopify’s UI components (optimized)
- Lazy load heavy content
- Minimize API calls
Don’ts
- Don’t fetch large datasets in checkout
- Don’t add multiple similar extensions
- Don’t use custom fonts (use system fonts)
- Don’t block checkout completion
Monitoring
Use Shopify’s extension analytics to monitor:
- Extension load time
- Error rates
- User interactions
- Conversion impact
Common Gotchas
1. Metafield Namespaces
Custom metafields need the custom namespace for checkout access:
{
namespace: 'custom', // Must be 'custom'
key: 'your_field',
valueType: 'string',
}
2. Cart Line Modification
You can add/remove cart lines, but some restrictions apply:
- Can’t modify prices directly (use Shopify Functions)
- Can’t remove the last item
- Quantity limits still apply
3. Testing Limitations
Development mode has limitations:
- Some features only work in deployed extensions
- Payment testing requires real payment methods
- Shipping rates need real carrier accounts
Tools and Resources
Official Documentation
- Shopify Checkout Extensibility - Complete API reference
- Shopify Functions - Backend logic documentation
- Web Pixels - Analytics and tracking
Development Tools
- Shopify CLI: Essential for extension development
- Polypane: Test checkout across viewports
- Shopify GraphiQL App: Test Admin API queries
Learning Resources
- Shopify Dev YouTube - Video tutorials
- Shopify Community Forums - Developer discussions
- Shopify Changelog - Latest updates
What to Watch For
Future Changes
Shopify continues to evolve Checkout Extensibility:
- New extension targets being added regularly
- More Shopify Functions types coming
- Improved performance and capabilities
Stay updated via:
- Shopify Dev newsletter
- Changelog RSS feed
- Partner dashboard announcements
Plus vs Non-Plus
Most features are available on all plans now, but some remain Plus-only:
- Some advanced Shopify Functions
- B2B checkout customizations
- Script Editor (legacy)
Check Shopify’s plan comparison for current feature availability.
Bottom Line
Checkout Extensibility is the future of Shopify checkout customization. It’s more powerful, more secure, and more maintainable than checkout.liquid ever was.
My recommendation:
- If you’re on checkout.liquid: Migrate now. The deadline has passed, and you’re accumulating technical debt.
- If you’re building new: Start with Extensibility. Don’t learn the old way.
- If you need complex customizations: Consider hiring a specialist. Checkout is too important to get wrong.
The learning curve is steeper than Liquid, but the results are worth it. You get upgradable, performant, secure checkout customizations that won’t break with every Shopify update.
New to Shopify development? Try Shopify with a 14-day free trial and start with Shopify’s Partner program for free development stores and learning resources.