Shopify Checkout Extensibility Is Here — What You Need to Rebuild Before It's Too Late — Jhango Blog
Technical

Shopify Checkout Extensibility Is Here — What You Need to Rebuild Before It's Too Late

Shopify has drawn a hard line in the sand: checkout.liquid is dead. If you're running a Shopify Plus store with custom checkout scripts, you are officially on borrowed time. The migration deadline to Checkout Extensibility has been enforced since August 2025, and any store still running legacy scripts is operating on a deprecated foundation that Shopify can break with any platform update.

This isn't a cosmetic change. It's a full architectural shift from injecting raw HTML/JS/CSS into the checkout to using a sandboxed, API-driven extension model. And if you've built anything custom in your checkout — gift with purchase logic, custom form fields, dynamic shipping rules, loyalty point redemption — every single one of those needs to be rebuilt.

We've migrated over 80 Shopify Plus checkouts at Jhango. This guide covers exactly what needs to change, the patterns we see most often, and the gotchas that catch teams off guard.

What Actually Changed (And Why Shopify Did It)

To understand the migration, you need to understand what Shopify replaced and why.

The Old World: checkout.liquid + Script Tags

Shopify Plus merchants had two primary customization tools:

  • checkout.liquid — A Liquid template that gave you full control over checkout HTML. You could inject anything: custom CSS, JavaScript, tracking pixels, UI modifications, and more.
  • Script Editor / Shopify Scripts — Ruby-based scripts that ran server-side to modify line items, shipping rates, and payment methods. Written in a restricted Ruby sandbox.
  • Additional Scripts — Post-purchase and order status page scripts injected via the admin.

This approach was powerful but created serious problems. Checkout modifications could break Shopify's payment processing, create security vulnerabilities, slow down checkout performance, and prevent Shopify from rolling out new checkout features like Shop Pay Installments or one-page checkout.

The New World: Checkout Extensibility

Checkout Extensibility replaces all of this with a structured, component-based system:

  • Checkout UI Extensions — React-based components rendered in designated extension points throughout the checkout flow
  • Shopify Functions — WebAssembly-compiled logic that replaces Scripts for discounts, shipping, and payment customization
  • Web Pixels — Sandboxed tracking scripts that replace raw pixel injection
  • Post-Purchase Extensions — Dedicated extension points for post-purchase upsells and thank-you page customization
  • Checkout Branding API — A structured API for visual customization (colors, fonts, spacing) instead of raw CSS
Critical Deadline

Shopify has already removed checkout.liquid access for new stores. Existing stores that haven't migrated risk breaking changes with every Shopify platform update. There is no extension on this — the migration is mandatory.

The 6 Most Common Customizations That Need Rebuilding

After migrating 80+ checkouts, these are the patterns we rebuild most frequently. If you have any of these, start planning now.

1. Gift With Purchase (GWP) Logic

One of the most common checkout customizations. The old approach used Shopify Scripts to automatically add a free product when cart conditions were met.

Before (Script Editor — Ruby):

# Old Script Editor approach
GIFT_PRODUCT_ID = 123456789
THRESHOLD = Money.new(cents: 100_00)

Input.cart.line_items.each do |line_item|
  next unless line_item.variant.id == GIFT_PRODUCT_ID
  if Input.cart.subtotal_price < THRESHOLD
    line_item.change_line_price(
      line_item.line_price,
      message: "Spend $100 to qualify for free gift"
    )
  else
    line_item.change_line_price(
      Money.zero,
      message: "Free gift with $100+ purchase!"
    )
  end
end

Output.cart = Input.cart

After (Shopify Function — JavaScript/Rust compiled to Wasm):

// shopify.extension.toml
[[extensions]]
type = "product_discounts"
name = "gift-with-purchase"

// src/run.js
export function run(input) {
  const GIFT_VARIANT_ID = "gid://shopify/ProductVariant/123456789";
  const THRESHOLD = 100.00;

  const subtotal = parseFloat(
    input.cart.cost.subtotalAmount.amount
  );

  if (subtotal < THRESHOLD) {
    return { discounts: [] };
  }

  const giftLine = input.cart.lines.find(line =>
    line.merchandise.__typename === "ProductVariant" &&
    line.merchandise.id === GIFT_VARIANT_ID
  );

  if (!giftLine) return { discounts: [] };

  return {
    discounts: [{
      targets: [{
        productVariant: { id: GIFT_VARIANT_ID }
      }],
      value: {
        percentage: { value: "100" }
      },
      message: "Free gift with $100+ purchase!"
    }]
  };
}
Key Difference

Shopify Functions run in a WebAssembly sandbox. They receive structured input (JSON), return structured output (JSON), and have strict execution time limits (max 5ms). You can't make network calls, access databases, or perform any I/O. All logic must be self-contained.

2. Custom Checkout Fields

Delivery instructions, gift messages, PO numbers, company tax IDs — any custom field you added to checkout needs to be rebuilt as a Checkout UI Extension.

Before (checkout.liquid):

<!-- Injected directly into checkout.liquid -->
<div class="custom-field-wrapper">
  <label for="delivery-notes">Delivery Instructions</label>
  <textarea id="delivery-notes" name="checkout[attributes][delivery_notes]"
    placeholder="Gate code, leave at door, etc."></textarea>
</div>

<script>
  // Save to cart attributes on change
  document.getElementById('delivery-notes')
    .addEventListener('change', function() {
      fetch('/cart/update.js', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
          attributes: { 'delivery_notes': this.value }
        })
      });
    });
</script>

After (Checkout UI Extension — React):

// extensions/delivery-notes/src/Checkout.jsx
import {
  reactExtension,
  TextField,
  useApplyAttributeChange,
  useAttributeValues,
} from "@shopify/ui-extensions-react/checkout";

export default reactExtension(
  "purchase.checkout.shipping-option-list.after.render",
  () => <DeliveryNotes />
);

function DeliveryNotes() {
  const applyAttributeChange = useApplyAttributeChange();
  const [notes] = useAttributeValues(["delivery_notes"]);

  return (
    <TextField
      label="Delivery Instructions"
      value={notes || ""}
      onChange={(value) => {
        applyAttributeChange({
          type: "updateAttribute",
          key: "delivery_notes",
          value: value,
        });
      }}
      placeholder="Gate code, leave at door, etc."
    />
  );
}

3. Dynamic Shipping Rate Modifications

If you used Scripts to hide, rename, or re-price shipping rates based on cart contents, location, or customer tags, those need to migrate to Delivery Customization Functions.

Before (Script Editor):

# Hide express shipping for heavy items
Input.shipping_rates.shipping_rates.each do |rate|
  if rate.name.include?("Express")
    total_weight = Input.cart.line_items.sum { |li| li.grams * li.quantity }
    if total_weight > 20_000 # Over 20kg
      rate.change_name("Express (Not available for heavy orders)")
      rate.apply_discount(rate.price, message: "")
    end
  end
end

Output.shipping_rates = Input.shipping_rates

After (Delivery Customization Function):

// src/run.js
export function run(input) {
  const MAX_WEIGHT_GRAMS = 20000;

  const totalWeight = input.cart.lines.reduce((sum, line) => {
    const weight = line.merchandise?.weight || 0;
    return sum + (weight * line.quantity);
  }, 0);

  if (totalWeight <= MAX_WEIGHT_GRAMS) {
    return { operations: [] };
  }

  // Hide express shipping options for heavy orders
  const hideOperations = input.cart.deliveryGroups
    .flatMap(group => group.deliveryOptions)
    .filter(option => option.title.includes("Express"))
    .map(option => ({
      hide: {
        deliveryOptionHandle: option.handle,
      }
    }));

  return { operations: hideOperations };
}

4. Payment Method Filtering

Hiding or reordering payment methods based on cart value, customer group, or product type. This moves from Scripts to Payment Customization Functions.

5. Loyalty Point Redemption

If you integrated a loyalty program directly into the checkout via checkout.liquid, you'll need to rebuild it as a combination of Checkout UI Extensions (for the UI) and Shopify Functions (for applying the discount).

6. Post-Purchase Upsells

Previously done with injected scripts on the thank-you page. Now handled through dedicated Post-Purchase UI Extensions that render between payment confirmation and the thank-you page.

Migration Gotchas That Catch Teams Off Guard

Even experienced Shopify developers hit these issues during checkout migration. Here's what to watch for:

Gotcha 1: Extension Points Are Fixed

In checkout.liquid, you could place custom UI anywhere. With Checkout UI Extensions, you can only render components at designated extension points. There are roughly 20 extension points available (before/after contact info, before/after shipping, before/after payment, etc.), but you cannot create arbitrary positions.

If your current customization sits in an unusual position, you'll need to find the closest available extension point or redesign the UX entirely.

Gotcha 2: No Direct DOM Access

Checkout UI Extensions render inside a sandboxed iframe. You cannot access the parent DOM, use document.querySelector(), inject arbitrary CSS, or modify Shopify's native checkout elements. Everything must go through the Checkout UI Extensions API.

Gotcha 3: Limited Component Library

You're restricted to Shopify's component library: TextField, Checkbox, Select, Banner, Divider, BlockStack, etc. Custom HTML elements are not supported. If your current checkout has a complex interactive widget, you may need to simplify the design.

Feature checkout.liquid Checkout Extensibility
UI Placement Anywhere in checkout Fixed extension points only
Styling Full CSS control Checkout Branding API only
JavaScript Full browser APIs Sandboxed, React-only
Server Logic Ruby Scripts Wasm Functions (5ms limit)
Tracking Pixels Raw script injection Web Pixels API
Network Calls Full fetch/XHR No I/O in Functions; limited in UI Extensions
Performance Variable (your code quality) Enforced (Shopify controls rendering)
Security Your responsibility Sandboxed by Shopify

Gotcha 4: Functions Have Hard Limits

Shopify Functions compiled to WebAssembly have strict constraints:

  • Execution time: Max 5ms per invocation
  • Memory: Limited allocation
  • No network I/O: You cannot call external APIs, databases, or services from within a Function
  • Input size: The cart input object can be large for stores with many line items — write efficient parsing logic

If your current Scripts call external APIs (checking inventory from an ERP, validating against a CRM, etc.), you'll need to restructure. The common pattern is to use metafields on products/customers to pre-cache the data that Functions need.

Gotcha 5: Testing Is Different

You can't just edit a Liquid file and refresh. Checkout UI Extensions require:

  1. A local development setup with shopify app dev
  2. A development store or partner test store
  3. Building and deploying the extension to test on a live checkout flow
  4. Version management when pushing updates to production

Budget extra time for development workflow changes. Your team needs to be comfortable with Node.js, React, and the Shopify CLI.

The Migration Timeline We Recommend

Based on 80+ migrations, here's the realistic timeline for different levels of checkout complexity:

Complexity What's Involved Timeline
Light Basic branding, 1-2 custom fields, simple discount scripts 2-3 weeks
Medium GWP logic, shipping modifications, payment filtering, loyalty integration 4-6 weeks
Heavy Complex multi-step logic, ERP integrations, B2B workflows, post-purchase upsells, A/B testing 8-12 weeks
Pro Tip

Don't try to replicate your checkout.liquid customizations 1:1. This is an opportunity to audit what actually drives conversions and drop the rest. We regularly find that 30-40% of legacy checkout customizations are either broken, unused, or actively hurting conversion rates.

Step-by-Step Migration Process

Step 1: Audit Your Current Checkout

Document every customization in your checkout.liquid file, Script Editor, and Additional Scripts. For each one, answer:

  • What business problem does this solve?
  • Is it still necessary? (Check analytics — is it actually being used?)
  • What's the equivalent in Checkout Extensibility?
  • Does it require a UI Extension, a Function, or both?

Step 2: Set Up the Development Environment

# Install Shopify CLI
npm install -g @shopify/cli @shopify/app

# Create a new app (or add to existing)
shopify app init

# Generate checkout UI extension
shopify app generate extension --type checkout_ui

# Generate a Shopify Function
shopify app generate extension --type product_discounts

# Start local development
shopify app dev

Step 3: Build and Test Extensions Individually

Migrate one customization at a time. Don't try to rebuild everything simultaneously. We typically follow this order:

  1. Checkout Branding — Get the visual look right first using the Checkout Branding API
  2. Shopify Functions — Rebuild discount, shipping, and payment logic
  3. UI Extensions — Add custom fields, banners, and interactive elements
  4. Web Pixels — Migrate tracking and analytics
  5. Post-Purchase — Rebuild thank-you page customizations

Step 4: Parallel Testing

Run the new Checkout Extensibility setup in parallel with your existing checkout (on a staging/development store) and compare behavior. Verify discount amounts, shipping rates, field data capture, and pixel firing.

Step 5: Deploy and Monitor

When ready, deploy the extensions to your production store and remove the legacy checkout.liquid code. Monitor checkout conversion rates, average order value, and error rates closely for the first two weeks.

When to Call In Specialists

You can handle this migration in-house if your team has experience with React, Node.js, and the Shopify CLI. But consider bringing in specialists if:

  • Your checkout has more than 5 distinct customizations
  • You have complex B2B logic (tiered pricing, net terms, PO numbers)
  • You integrate with external systems (ERP, CRM, loyalty platforms) at checkout
  • Your store does over $1M/year and checkout downtime has a real revenue impact
  • Your team isn't comfortable with React or WebAssembly

"We've rebuilt checkout customizations for brands processing $50M+ annually. The investment in getting this right pays for itself in the first month through improved checkout performance and conversion rates."

Jhango's checkout team has migrated stores across fashion, beauty, food and beverage, B2B wholesale, and subscription commerce. We understand the nuances of each vertical and can identify which customizations to keep, which to drop, and which to improve during the migration.

Ready to migrate your checkout? Learn about our Checkout Extensibility services or reach out for a free checkout audit. We'll map every customization and give you a clear migration plan with timeline and costs.

J
Jhango Team
Shopify Development Experts

Jhango is India's leading Shopify development agency. Our team of 50+ experts has built, migrated, and scaled over 600 Shopify stores across industries. We specialize in custom theme development, performance optimization, B2B solutions, and conversion rate optimization.

Need Help With Checkout Extensibility?

Get a free checkout audit from Jhango's expert team. We'll map every customization and give you a clear migration plan.

Get Free Checkout Audit