Checkout Extensibility: The End of checkout.liquid
Shopify has killed checkout.liquid. The new era is powered by Rust, WebAssembly, and Sandboxed UI Extensions. A technical upgrade guide.
For a decade, checkout.liquid was the ultimate power tool for Shopify Plus developers.
It gave us raw HTML/JS access to the checkout.
We abused it.
We engaged in “DOM Scraping” to hide shipping rates.
We used setInterval loops to hack in free gifts.
We injected 50 different tracking pixels that slowed the page load to 8 seconds.
It was flexible, but it was fragile and insecure.
Shopify has deprecated it. The replacement is Checkout Extensibility. This is not just an upgrade; it is a Major shift. We are moving from “Client-Side Hacks” to “Server-Side Functions”. We are moving from “jQuery” to “Rust & React”.
At Maison Code Paris, we have migrated over 50 Plus merchants to Extensibility. Here is the engineering guide.
Why Maison Code Discusses This
At Maison Code Paris, we act as the architectural conscience for our clients. We often inherit “modern” stacks that were built without a foundational understanding of scale. We see simple APIs that take 4 seconds to respond because of N+1 query problems, and “Microservices” that cost $5,000/month in idle cloud fees.
We discuss this topic because it represents a critical pivot point in engineering maturity. Implementing this correctly differentiates a fragile MVP from a resilient, enterprise-grade platform that can handle Black Friday traffic without breaking a sweat.
1. The Architecture: Sandboxed Power
Why did Shopify do this?
- Security: Scripts in
checkout.liquidcould technically steal credit card numbers. Extensions are sandboxed. - Performance: Extensions run in a Web Worker or on the Server (WASM). The main thread stays free.
- Upgrade Safety: Because you can’t touch the DOM, Shopify can redesign the checkout entirely (e.g., One Page Checkout) and your extension will just “work”.
There are two main components:
- UI Extensions: Rendering pixels on the screen (React-like).
- Functions: Business Logic on the server (Rust/WASM).
2. UI Components: The “Remote Rendering” Model
You write code that looks like React, but it is not rendering to the DOM directly.
It sends a JSON tree to Shopify, which renders Native Components.
This is why you cannot use <div> or classname. You must use Shopify’s Primitives.
// extensions/gift-message/Checkout.jsx
import {
reactExtension,
TextField,
BlockStack,
Text,
useApplyAttributeChange
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension('purchase.checkout.block.render', () => <GiftMessage />);
function GiftMessage() {
const applyChange = useApplyAttributeChange();
const handleInput = (value) => {
applyChange({
type: 'updateAttribute',
key: '_gift_note',
value: value
});
};
return (
<BlockStack spacing="tight">
<Text size="medium" weight="bold">Is this a gift?</Text>
<TextField label="Gift Note" multiline onChange={handleInput} />
</BlockStack>
);
}
Key Constraint: You cannot access window or document.
If you need to fetch data, you must use the useQuery hook provided by the sandbox, or make fetch calls to your own proxy.
3. Shopify Functions: Logic at the Edge (Rust)
This is the most powerful part. Previously, to do “Tiered Discounts” (Buy 5 get 20% off), you used Shopify Scripts (Ruby). Scripts were buggy, hard to test, and had strict CPU limits. Shopify Functions are compiled to WebAssembly (Wasm). They execute in <5ms.
We write them in Rust because it is the most performant language for Wasm.
Case Study: Volume Discount
// src/run.rs
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function]
fn run(input: input::ResponseData) -> Result<output::FunctionResult> {
let mut discounts = vec![];
for line in input.cart.lines {
let quantity = line.quantity;
// Define Logic
let percentage = if quantity >= 50 {
25.0 // Wholesale
} else if quantity >= 10 {
10.0 // Bulk
} else {
0.0
};
if percentage > 0.0 {
discounts.push(Discount {
value: DiscountValue::Percentage(percentage),
message: format!("Volume Discount ({}%)", percentage),
targets: vec![Target::ProductVariant(line.merchandise.id)],
});
}
}
Ok(output::FunctionResult {
discounts,
discount_application_strategy: DiscountApplicationStrategy::Maximum,
})
}
This binary is uploaded to Shopify. It scales infinitely.
4. Validation: Blocking Bad Orders
In the old days, to prevent P.O. Box shipping, we used JS validation. Savvy users (or bots) disabled JS and ordered anyway. Now, we use Cart Validation Functions. This runs on the backend before the checkout can proceed.
# Input Query
query Input {
cart {
deliveryGroups {
deliveryAddress {
address1
city
countryCode
}
}
}
}
If the rust function returns an error, the “Pay Now” button is disabled at the API level. It is impossible to bypass.
5. Web Pixels: The Tracking Fix
Marketing teams hate migration because “We will lose our GTM tracking!”
Actually, Extensibility fixes tracking.
The Web Pixels API subscribes to checkout events (checkout_completed, payment_info_submitted) in a secure sandbox.
Because it uses the Browser Beacon API, it is surprisingly resistant to Ad Blockers.
We see a 15% lift in tracked conversions after migrating to Web Pixels.
// analytics.js
analytics.subscribe('checkout_completed', (event) => {
const total = event.data.checkout.totalPrice.amount;
// Send to GA4
gtag('event', 'purchase', { value: total });
});
6. Headless Checkout vs Extensibility
For years, “Headless Checkout” was the only way to get customization. But it was dangerous. You became responsible for PCI Compliance. Checkout Extensibility kills the need for Headless Checkout. You get 90% of the flexibility (Custom Fields, Upsells, Logic) with 0% of the maintenance. Unless you are Nike or Apple, you do not need a custom checkout build. Sticking to the Platform (Shopify) guarantees you get Apple Pay, PayPal, and Shop Pay updates instantly.
7. Global Markets: Localized Checkout
Selling in France vs USA?
- USA: Needs “State” dropdown.
- France: Needs “Cedex” handling.
- Japan: Needs “Prefecture”.
With Extensibility, we use the Localization API.
Our extensions render different fields based on the
context.market. We can show a “Leave at Door” instruction for US orders, but hide it for German orders (where it might be illegal/uncommon). This is dynamic rendering at the edge.
8. Branding API: No More CSS
You can no longer edit checkout.css or checkout.scss.
You use the Branding API (GraphQL).
You define:
- Primary Colors.
- Corner Radii (rounded vs square).
- Fonts (Upload WOFF2).
- Header/Footer Layouts.
The “Skeptic’s View”: “But I can’t move the button 5px to the left!” Counter-Point: Good. You shouldn’t. Every time you hack the layout, you break Accessibility. Shopify has spent millions testing the optimal layout. Trust the platform. Focus on the Content and Logic, not the padding.
7. Post-Purchase Extensions: The Golden Moment
The checkout isn’t over when they pay.
The Thank You Page is the highest converting real estate in e-commerce.
Open Rate: 100%. Attention: 100%.
With Extensibility, we can inject a “One-Click Upsell” here.
“You bought the Sneakers. Do you want the Cleaner for $10? (No shipping cost).”
Because we have the order_id and the payment_token, we can process this transaction without re-entering CVV.
This feature alone pays for the entire migration cost in 1 month.
8. Testing: The Sandbox Environment
In the old liquid days, we tested in production. Terrifying.
Now, we have the Partner Dashboard Sandbox.
You can simulate:
- Slow Network (3G).
- API Errors (Inventory Failure).
- Different Markets (USD vs EUR).
We write Unit Tests for our Rust Functions.
cargo testruns in 10ms. We prove that “Buy 5 get 10% off” works mathematically before we deploy it to a store processing $1M/day.
9. Conclusion
Checkout Extensibility is a filter. It filters out the “Hackers” and keeps the “Engineers”. It forces you to write clean, modular, testable code. The result is a checkout experience that is faster, safer, and ready for the next 10 years of e-commerce.
Panic about the deadline?
The checkout.liquid deprecation date is approaching.
Book your Migration Audit. Read about B2B Pricing and Clean Code.
Migrating a complex store takes 4-8 weeks.
- Audit: Export
checkout.liquidand map every<script>block.- Google Ads -> Web Pixels.
- Shipping Logic -> Delivery Functions.
- Upsell Slider -> UI Extension.
- Prioritize: “Must Haves” vs “Legacy Junk”. Usually 30% of scripts are dead code.
- Develop: Build extensions locally using
shopify app dev. - A/B Test: You can publish the new checkout to a “Draft” status and preview it.
- Go Live: Switch the flag in Checkout Settings.
8. Conclusion
Checkout Extensibility is a filter. It filters out the “Hackers” and keeps the “Engineers”. It forces you to write clean, modular, testable code. The result is a checkout experience that is faster, safer, and ready for the next 10 years of e-commerce.
Panic about the deadline?
The checkout.liquid deprecation date is approaching.