Shopify Checkout Extensibility Migration: What Breaks, What Wins, and What to Do About Custom Scripts
Checkout.liquid is gone for Plus merchants in 2025, and the extensibility model isn't a drop-in replacement. Here's what actually breaks, what you gain, and how to migrate without torching conversion.
If you're on Shopify Plus and still running a customized checkout.liquid, the countdown already ended. Shopify sunset the old checkout customization model for the Information, Shipping, and Payment pages in August 2024, and the Thank You and Order Status pages followed in August 2025. If you haven't migrated yet, you're on the upgraded checkout whether you signed off on it or not — and a lot of the behavior your merchandising team relied on is quietly gone.
This is the honest version of that migration: what breaks, what you actually gain, and where the new model still hurts.
Why Shopify Forced This
The old checkout.liquid file was a liability. Every Plus merchant had a snowflake — hand-rolled JS injections, jQuery from 2018, third-party pixels stacked six deep, and no upgrade path when Shopify wanted to ship new payment methods or accessibility fixes. Checkout is the single most conversion-sensitive surface in the store, and Shopify couldn't iterate on it without breaking someone's custom trust-badge widget.
Checkout Extensibility replaces that free-for-all with three constrained primitives:
- Checkout UI Extensions — React (Preact, really) components that render in predefined slots
- Shopify Functions — server-side logic in Wasm for discounts, delivery, and payment customizations
- Web Pixels — a sandboxed API for analytics and marketing tags
Everything runs in isolated iframes or sandboxes. You cannot inject arbitrary JS into the checkout DOM anymore. This is a feature, even when it feels like a punishment.
What Actually Breaks in Migration
We've walked several mid-market Plus merchants through this. The predictable casualties, roughly ranked by how often they blow up a launch:
1. Custom field capture
If you were collecting gift messages, VAT numbers, PO numbers, or delivery instructions via injected inputs on the shipping page, that code is dead. The replacement is a UI extension using the BuyerJourney and Checkout APIs to write to cart attributes or order metafields. Straightforward, but you need to rebuild validation, error states, and the analytics events that fired on blur.
2. Third-party scripts
Any script tag you had loading on checkout — Hotjar session replay, custom Klaviyo hooks, fraud tools that weren't official partners, homegrown A/B testing — none of it can run in the new checkout DOM. Shopify's Web Pixels API covers analytics use cases, but session replay and DOM-manipulating tools are structurally incompatible. This is where most merchants lose data continuity for a quarter.
3. Visual customization beyond the settings panel
Checkout Editor gives you brand colors, typography, corner radius, and section ordering. That's it. If you had a custom order summary layout, floating trust badges tucked between form fields, or a countdown timer above the pay button, you're rebuilding with UI extensions in the slots Shopify permits — and there are fewer slots than you'd like.
4. Script Editor discount logic
Shopify Scripts is being deprecated alongside checkout.liquid. Tiered discounts, spend-X-get-Y logic, and payment gateway hiding based on cart contents all move to Shopify Functions. The migration is doable but non-trivial: Functions are Wasm modules (Rust or JavaScript via Javy), version-controlled, and deploy through the CLI. Your marketing ops person who edited Ruby in the admin is not going to be editing this.
5. Analytics attribution
This one bites quietly. Any GTM container you had firing on the checkout pages needs to be reimplemented as a custom Web Pixel. The sandbox limits what you can read from the DOM, and events fire on a different schedule than you're used to. Expect your conversion numbers in GA4 or your warehouse to look wrong for two weeks while you reconcile.
What You Actually Gain
It's not all pain. The upgraded checkout ships with genuine wins that are hard to replicate on a custom stack.
- Better Core Web Vitals out of the box. LCP on the payment page typically drops meaningfully once you strip the injected scripts. In our engagements, we've seen checkout LCP move from the 3–4s range to well under 2s just by removing the old customization layer.
- Shop Pay optimizations land automatically. One-tap checkout, network tokenization, and installments UI keep improving without your team doing anything.
- Accessibility. The new components are keyboard-navigable and screen-reader tested. Your old checkout probably wasn't, and you probably weren't going to fix it.
- App-based customizations from the ecosystem — subscriptions, upsells, address validation — install through the Checkout Editor with a toggle instead of a developer ticket.
A Migration Sequence That Doesn't Explode
Here's the order we run these projects. It's boring on purpose.
Step 1: Audit the old checkout
Before you touch anything, list every customization. Grep the old checkout.liquid for <script>, additional_scripts, and any Liquid conditionals. Then interview marketing, ops, and finance about what they think is happening at checkout. The two lists will not match.
Step 2: Classify each item
For every customization, decide:
- Rebuild as a UI extension (custom field, informational message, upsell)
- Rebuild as a Function (discount, shipping rule, payment method hiding)
- Rebuild as a Web Pixel (analytics, marketing tag)
- Replace with an app (subscriptions, address autocomplete, fraud)
- Drop it — you'd be surprised how much of the old checkout was cruft nobody asked for
Step 3: Scaffold extensions locally
Use the Shopify CLI. A minimal UI extension for capturing a gift message looks roughly like this:
import {
reactExtension,
TextField,
useApplyAttributeChange,
useAttributeValues,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.shipping-option-list.render-after',
() => <GiftMessage />
);
function GiftMessage() {
const [current] = useAttributeValues(['gift_message']);
const applyAttributeChange = useApplyAttributeChange();
return (
<TextField
label="Gift message (optional)"
value={current ?? ''}
onChange={(value) =>
applyAttributeChange({
key: 'gift_message',
type: 'updateAttribute',
value,
})
}
maxLength={200}
/>
);
}
Note the extension target string. Shopify defines a fixed set of these, and you can only render where they let you. Read the target reference before you promise a designer anything.
Step 4: Rebuild Scripts as Functions
A discount Function in JavaScript looks like this, roughly:
export function run(input) {
const cartTotal = parseFloat(input.cart.cost.subtotalAmount.amount);
if (cartTotal < 100) return { discounts: [] };
return {
discounts: [{
targets: [{ orderSubtotal: { excludedVariantIds: [] } }],
value: { percentage: { value: 10 } },
message: '10% off orders over $100',
}],
discountApplicationStrategy: 'FIRST',
};
}
Deploy through shopify app deploy. Functions get versioned, which means you can actually roll back — a luxury Scripts never gave you.
Step 5: Parallel-run analytics
Don't cut over your Web Pixel on launch day. Run the new pixel alongside your legacy tags for at least two weeks, compare event volumes daily, and only decommission the old stack once the numbers reconcile within a percent or two.
Step 6: Soft launch to a percentage of traffic
Shopify lets you publish the new checkout to a subset of sessions. Use it. Start at 10%, watch conversion rate and average time-to-checkout for a week, then ramp. If conversion drops more than a point, something in your rebuild is worse than the old version — probably a missing trust element or a field you forgot to re-validate.
The Traps Nobody Tells You About
A few things we've been burned by:
- Extension performance budgets are real. Shopify throttles extensions that render slowly or fetch excessively. Cache aggressively and keep network calls off the render path.
- You cannot read PII from extensions. No credit card fields, no full addresses in some contexts. Design your UX around what the sandbox actually exposes.
- B2B checkout is a different beast. If you're on Shopify B2B, some extension targets don't exist yet. Verify before scoping.
- The Checkout Editor is per-market. If you run multiple markets, you're configuring each one. Budget the time.
Where We'd Start
If you're staring at this migration on a Monday morning: pull the audit together this week, classify every customization by end of next, and get a scaffolded extension running locally before you write a single ticket for engineering. The migration itself is a two-to-six week project for a typical mid-market Plus store — most of that is analytics reconciliation and QA, not code. If you want a second pair of eyes on the plan, our team runs these regularly through our e-commerce engineering practice. The worst version of this project is the one where marketing finds out on go-live day that their favorite widget is gone.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading
Cart Abandonment Recovery on Shopify: What Actually Moves the Needle in 2026
Most cart abandonment recovery advice is stuck in 2019. Here's what we've seen actually recover revenue on Shopify stores in 2026 — and what's a waste of engineering time.
Shopify Collection Pages at 10,000 SKUs: Faceted Filtering Without Killing TTFB
Faceted filtering on large Shopify catalogs quietly destroys collection page performance. Here's how we architect it to keep TTFB under 400ms without a full replatform.
Shopify Functions vs Scripts: What Actually Runs at Checkout in 2026
Scripts are gone, Functions are the new contract. Here's what we learned porting discount logic, delivery customizations, and payment gating to Shopify Functions — and where the model still hurts.
