Shopify & E-Commerce15 min readSeptember 5, 2026

Migrating to Shopify Checkout Extensibility: Building Custom Functions with WebAssembly

The complete, production-grade migration blueprint away from deprecated checkout.liquid to modern Shopify Functions, UI Extensions, and WebAssembly-powered checkout validation.

Nazmul Hawlader
Nazmul Hawlader
Senior Shopify & Full-Stack Engineer
Note: Key takeaways in this guide: • Understand the architectural limitations that led to the deprecation of legacy checkout.liquid. • Write, test, and deploy Shopify Functions in TypeScript using the Javy WebAssembly compiler. • Build server-side payment and delivery customization rules that execute in under 5 milliseconds. • Develop custom checkout UI extensions using Shopify’s sandboxed component library. • Store merchant configuration dynamically in Metafields without hardcoding logic into codebases. • Execute a zero-downtime migration for high-volume Shopify Plus enterprise stores.

For nearly a decade, the gold standard of Shopify Plus enterprise customization was `checkout.liquid`. It gave high-growth merchants direct access to the checkout DOM, allowing developers to inject custom JavaScript trackers, manipulate payment gateways, insert custom fields, and alter styles. However, this flexibility came with catastrophic architectural flaws: third-party scripts frequently conflicted, security vulnerabilities exposed payment tokens to client-side injection, and mobile checkout performance degraded during peak flash sales.

To solve these fundamental security and performance problems once and for all, Shopify introduced Checkout Extensibility and Shopify Functions. Checkout Extensibility completely deprecates `checkout.liquid` in favor of a modern, sandboxed, and upgrade-safe architecture. Instead of running untrusted client-side JavaScript in the browser DOM, backend business logic is executed as compiled WebAssembly (Wasm) binaries directly on Shopify’s global edge servers in under 5 milliseconds.

Migrating to Checkout Extensibility is not optional—it is a mandatory requirement for all Shopify Plus merchants. However, the paradigm shift from DOM manipulation to WebAssembly-based Functions and sandboxed UI Extensions can be daunting for engineering teams accustomed to legacy themes.

In this comprehensive architectural guide, I provide a complete, step-by-step masterclass on modern Shopify Checkout Extensibility. You will learn how Shopify Functions execute in WebAssembly, how to write custom payment and cart validation rules in TypeScript, how to build sandboxed UI extensions, and how to execute a zero-downtime enterprise migration.

1. The Sunset of checkout.liquid: Why Shopify Overhauled the Checkout Architecture

To understand modern Checkout Extensibility, you must appreciate the profound security and engineering liabilities inherent in the legacy `checkout.liquid` model.

In the old architecture, developers had direct access to the HTML `<head>` and `<body>` of the checkout page. While this made adding a custom datepicker or trust badge trivial via jQuery, it opened massive attack vectors: - Client-Side Script Injection (Magecart Attacks): Malicious third-party scripts, compromised tracking pixels, or infected Chrome extensions could inspect form inputs and scrape unencrypted credit card numbers directly from the DOM. - Checkout Brittleness & Breaking Changes: Whenever Shopify deployed checkout optimizations or introduced one-page checkout, custom scripts written for legacy multi-step layouts broke instantly, costing merchants millions of dollars in abandoned transactions. - Slow Checkout Load Times: Legacy checkouts routinely loaded 2MB of third-party analytics and optimization scripts, delaying payment submission by 2 to 4 seconds during high-concurrency flash sales.

The Checkout Extensibility Revolution Checkout Extensibility replaces direct DOM access with four isolated, sandboxed pillars: - 1. Shopify Functions: Replaces Shopify Scripts with compiled WebAssembly backend logic running on Shopify servers. - 2. Checkout UI Extensions: Sandboxed UI components that render natively inside designated checkout extension points. - 3. Web Pixels API: Executes analytics and tracking tags inside isolated Web Workers, completely off the main checkout thread. - 4. Branding API: Programmatic styling engine that applies fonts, colors, and border radii natively to the checkout interface without custom CSS injection.

Pro Tip: Shopify has officially deprecated checkout.liquid for Information, Shipping, and Payment pages on Shopify Plus. All enterprise stores must migrate to Checkout Extensibility to maintain compliance and access Shopify’s latest one-page checkout features.

2. The Mechanics of Shopify Functions: Server-Side WebAssembly (Wasm) Runtime

Shopify Functions are the most exciting backend innovation in the modern Shopify ecosystem. Previously, merchants customized cart discounts and shipping options using Shopify Scripts (written in Ruby). However, Ruby scripts ran with significant execution latency and could only execute on the primary Shopify cluster.

Shopify Functions completely reinvent server-side customization by compiling your code into WebAssembly (Wasm).

Why WebAssembly Changes Everything - Extreme Performance: Compiled Wasm binaries execute in under 5 milliseconds. Shopify can evaluate hundreds of thousands of checkout calculations simultaneously without degrading server latency. - Deterministic Security: WebAssembly runs in a strictly sandboxed virtual machine with zero access to the host file system, network sockets, or environment variables. Functions cannot leak merchant data or introduce security vulnerabilities. - Multi-Language Support: Because WebAssembly is an open binary instruction format, developers can author Shopify Functions in Rust, TypeScript (via Javy), or Zig. - Global Edge Execution: Functions execute directly on the nearest edge node processing the customer’s checkout, eliminating cross-ocean network latency.

Pro Tip: While Rust produces the absolute smallest Wasm binary sizes, TypeScript paired with Javy (Shopify’s open-source JavaScript-to-WebAssembly compiler) offers the fastest developer velocity and allows full-stack teams to reuse their existing TypeScript domain types.

3. Authoring Your First Function in TypeScript: The Payment Customization API

Let us examine a real-world enterprise requirement: a luxury brand wants to hide Cash on Delivery (COD) and Bank Wire Transfer payment options if the customer’s cart total exceeds $2,500 or if the cart contains high-risk product categories.

In the modern architecture, this is implemented using the Payment Customization Function API.

The Anatomy of a Function Project A Shopify Function consists of three primary files: - 1. `shopify.extension.toml`: Configuration file declaring the function API type, targets, and Wasm binary location. - 2. `run.graphql`: Input query that declares the exact cart and order fields your function requires from Shopify. - 3. `run.ts`: Pure functional business logic that receives the query input and returns an array of mutation operations (such as `hide`, `rename`, or `move`).

extensions/payment-customization/src/run.ts
import {
  RunInput,
  FunctionRunResult,
  PaymentCustomizationOperation,
} from '../generated/api';

const MAX_CASH_ON_DELIVERY_THRESHOLD = 2500.0;

export function run(input: RunInput): FunctionRunResult {
  const operations: PaymentCustomizationOperation[] = [];

  // Parse cart total amount
  const cartTotal = parseFloat(input.cart.cost.totalAmount.amount);

  // Identify high-risk payment methods to hide
  for (const method of input.paymentMethods) {
    const isCodMethod = method.name.toLowerCase().includes('cash on delivery');
    const isWireMethod = method.name.toLowerCase().includes('bank transfer');

    // Rule: Hide COD or Wire Transfer for high-value carts
    if ((isCodMethod || isWireMethod) && cartTotal > MAX_CASH_ON_DELIVERY_THRESHOLD) {
      operations.push({
        hide: {
          paymentMethodId: method.id,
        },
      });
    }
  }

  return { operations };
}

This function executes deterministically in under 3ms. If the cart total exceeds $2,500, it returns a hide operation that removes risky payment methods from the checkout UI.

Pro Tip: Always test your function locally using the Shopify CLI command: "shopify app function build && shopify app function run < input.json". This verifies your logic against mock test payloads in milliseconds without deploying.

4. Building Sandboxed Checkout UI Extensions with Preact

While Shopify Functions handle backend calculations, Checkout UI Extensions handle frontend visual components. Unlike legacy checkouts where developers inserted arbitrary HTML, UI Extensions execute in a secure Web Worker sandbox and communicate with the checkout thread using an optimized Remote Procedure Call (RPC) bridge.

The Checkout Component Library Shopify provides a rich library of accessible, pre-styled components: `Banner`, `BlockStack`, `Checkbox`, `DatePicker`, `Select`, and `TextField`.

UI Extensions cannot load custom external CSS stylesheets or third-party web fonts. Instead, your components automatically inherit the merchant’s brand typography, color palette, and corner radii configured through the Shopify Checkout Branding API. This guarantees that your extension looks 100% native on every merchant store.

Target Extension Points Shopify defines precise visual insertion slots called Extension Points: - `purchase.checkout.shipping-option-list.render-after`: Perfect for delivery instruction textboxes. - `purchase.checkout.reductions.render-before`: Ideal for loyalty points redemption widgets. - `purchase.checkout.block.render`: Embeds directly in the main checkout column.

extensions/delivery-instructions/src/Checkout.tsx
import React, { useState } from 'react';
import {
  reactExtension,
  useApplyMetafieldsChange,
  useMetafield,
  BlockStack,
  TextField,
  Text,
  Checkbox,
} from '@shopify/ui-extensions-react/checkout';

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

function DeliveryInstructionsExtension() {
  const [requireSignature, setRequireSignature] = useState(false);
  const [notes, setNotes] = useState('');
  const applyMetafieldsChange = useApplyMetafieldsChange();

  const handleNotesChange = async (value: string) => {
    setNotes(value);
    // Persist note into checkout metafield
    await applyMetafieldsChange({
      type: 'updateMetafield',
      namespace: 'custom_delivery',
      key: 'instructions',
      valueType: 'string',
      value,
    });
  };

  return (
    <BlockStack spacing="base">
      <Text size="medium" weight="bold">Delivery Preferences</Text>
      <Checkbox
        checked={requireSignature}
        onChange={(checked) => setRequireSignature(checked)}
      >
        Require signature upon delivery (+$5.00)
      </Checkbox>
      <TextField
        label="Gate Code or Special Instructions"
        value={notes}
        onChange={handleNotesChange}
        multiline={2}
      />
    </BlockStack>
  );
}

This extension renders an interactive delivery preferences box and saves inputs directly into order metafields via useApplyMetafieldsChange without requiring an external backend database.

Pro Tip: Always validate character length on user text fields in checkout extensions to avoid truncating notes when exporting to third-party shipping fulfillment software like ShipStation.

5. Dynamic Configuration: Powering Functions via Metafields

A common mistake among junior developers is hardcoding business rules (such as thresholds, country codes, or discount tiers) directly into the function’s TypeScript or Rust code. If the merchant wants to change their threshold from $2,500 to $3,000, hardcoding requires a code commit, rebuild, and re-deployment.

In modern Shopify architecture, all Function settings must be dynamic and driven by Metafields.

The Metafields-Driven Architecture - 1. Metafield Definition: Define a custom Metafield on the App Installation (`$app:payment_settings`). - 2. GraphQL Input Query: Include the metafield in your `run.graphql` query. Shopify automatically injects the stored JSON into your function input at runtime. - 3. Admin Configuration UI: Build a clean Polaris page in your embedded app where merchants can adjust thresholds, toggle checkboxes, and select payment gateways. When the merchant saves, your app updates the Metafield via GraphQL.

This pattern provides merchants with complete administrative control while keeping your compiled WebAssembly binary completely stateless and reusable across thousands of stores.

Pro Tip: Store complex configuration settings as a single "json" type Metafield rather than multiple separate string metafields. This simplifies schema updates and reduces GraphQL input complexity points.

6. Performance & Security Guarantees: Sub-5ms Execution

The primary engineering victory of Checkout Extensibility is performance predictability. Under legacy `checkout.liquid`, a slow third-party analytics script could freeze the customer’s browser, causing payment gateway timeouts.

With Checkout Extensibility, Shopify enforces strict runtime budgets: - Function Execution Limit: Any Shopify Function that takes longer than 5 milliseconds to execute is automatically aborted by the Wasm runtime, and checkout proceeds with default rules. - Wasm Binary Size Limit: Compiled binaries must remain under 256KB to ensure instant instantiation. - Zero DOM Access: UI Extensions execute in Web Workers and cannot access `window`, `document`, or `localStorage`. This architectural isolation guarantees that customer payment credentials and PII (Personally Identifiable Information) can never be intercepted by malicious third-party code.

These guarantees mean that even during unprecedented flash sales—such as celebrity product drops generating 40,000 checkouts per minute—the checkout engine never falters.

Pro Tip: Avoid heavy external NPM libraries inside your Shopify Function codebase. Stick to native TypeScript arithmetic and string operations to keep your compiled Wasm binary under 80KB.

7. Step-by-Step Enterprise Migration Strategy: Zero-Downtime Blueprint

For an enterprise Shopify Plus merchant processing tens of millions in annual revenue, migrating from `checkout.liquid` to Checkout Extensibility requires rigorous change management.

The 5-Phase Migration Blueprint - Phase 1: Comprehensive Script Audit: Catalog every custom script currently living in `checkout.liquid`. Categorize each script into: (1) Tracking Pixels, (2) UI Modifications, (3) Payment/Shipping Customizations, or (4) Abandoned/Unused code. - Phase 2: Web Pixels Migration: Move Google Tag Manager, Meta Pixel, and TikTok tracking to Shopify’s native Web Pixels Manager. Verify event firing in the browser console. - Phase 3: Function & Extension Development: Build the required Shopify Functions and UI Extensions in your staging app environment. Write unit tests for all edge cases. - Phase 4: Staging Validation via Draft Checkouts: Test all checkout permutations using Shopify’s Draft Checkouts feature. Verify that payment customization rules trigger accurately across various cart totals, countries, and customer tags. - Phase 5: Production Cutover: Publish your Checkout Extensibility profile in the Shopify Admin. Monitor real-time conversion rates and payment completion metrics in Shopify Analytics for the subsequent 48 hours.

Pro Tip: Shopify allows you to maintain both your legacy checkout and a draft Checkout Extensibility profile simultaneously in your admin. Use this preview capability to conduct thorough stakeholder reviews before going live.

Summary & Key Conclusion

Shopify Checkout Extensibility and WebAssembly-powered Functions represent the future of modern e-commerce engineering. By eliminating legacy DOM vulnerabilities, enforcing strict sub-5ms execution limits, and delivering native, accessible UI components, Shopify has created the world’s most secure and performant checkout ecosystem.

Mastering these technologies transitions you from a standard theme developer into an elite e-commerce systems architect capable of delivering mission-critical enterprise transformations for the world’s largest brands.

Frequently Asked Questions

What is the absolute deadline for migrating away from checkout.liquid?

Shopify officially set the upgrade deadline for Information, Shipping, and Payment pages on Shopify Plus for August 2024, with Thank You and Order Status pages scheduled for complete deprecation in 2025. All Plus stores must migrate immediately to avoid feature lockouts.

Can I write Shopify Functions in JavaScript or TypeScript instead of Rust?

Yes. Using Shopify’s Javy compiler, you can write native TypeScript code that automatically compiles into standard WebAssembly (Wasm) bytecode during the build process, offering full TypeScript type safety and rapid development cycles.

Can Checkout UI Extensions make external API calls to third-party servers?

Yes, provided your app requests network permissions in its shopify.extension.toml configuration and the merchant approves the network access scope during app installation. Network calls run securely in the background worker thread.

Do Shopify Functions work on standard Shopify plans, or only Shopify Plus?

Public apps distributed via the Shopify App Store can deploy Shopify Functions (such as custom discount logic) to all Shopify merchants across Basic, Shopify, Advanced, and Plus plans. Custom private Functions remain exclusive to Shopify Plus.

Nazmul Hawlader

Written by Nazmul Hawlader

Top Rated

Senior Full-Stack Engineer & Official Shopify App Store developer. Founder of Stockly and Kilo (kilo.nazmulcodes.org), specializing in high-performance Shopify apps, client-side media compression, and sub-second web performance.

Recommended Related Articles