Commerce & Automation · 10 min read

Shopify Checkout Extensibility Migration After the Deadlines

Every legacy checkout path has now been switched off. Here is how to find what broke and rebuild it with checkout UI extensions, Shopify Functions and web pixels.

By Praful Patel

Short answer

By October 2026 every legacy checkout customisation path on Shopify has been switched off, so a migration now means rebuilding what already broke, not planning a cutover. checkout.liquid, additional scripts and order-status script tags are gone for Plus and non-Plus stores, and Shopify Scripts stopped executing on 30 June 2026. The replacements are checkout UI extensions for interface, Shopify Functions for business logic and web pixels for tracking.

Most writing about checkout extensibility was published as countdown content: upgrade before the deadline. The deadlines have now passed. If you are reading this in late 2026, you are probably in one of three positions: a non-Plus store that was auto-upgraded in August and lost its thank-you page tracking, a Plus store whose Ruby Scripts stopped running in June, or an agency inheriting a store where nobody is sure what used to happen at checkout. This guide is written for that situation.

Where the deadlines landed

These dates come from Shopify’s own changelog and documentation, not agency summaries, which disagree with each other more than you would expect.

What Plus stores Non-Plus stores Replacement
checkout.liquid on Information, Shipping and Payment steps Unsupported since August 2024 Never available Checkout UI extensions, branding API
checkout.liquid and additional scripts on Thank you / Order status Sunset 28 August 2025 Sunset 26 August 2026, with automatic upgrade Checkout and customer account UI extensions, web pixels
Script tags on the Order status page Stopped 28 August 2025 Stopped 26 August 2026 Customer account UI extensions, web pixels
Shopify Scripts (Ruby) No edits after 15 April 2026; stopped executing 30 June 2026 n/a (Plus feature) Shopify Functions
React-based checkout UI extensions (API 2025-07 and earlier) Shopify warns that extensions not upgraded to Polaris web components are blocked from updating after 1 October 2026 Preact plus Polaris web components

The authoritative sources are the checkout.liquid reference, which now carries the sunset notice, and the Shopify Scripts deprecation post. One more date sits just ahead: storefront script tags stop being injected on 1 March 2027, and scriptTagCreate has returned an error since 1 October 2026. That is a theme problem rather than a checkout one, but it lands on the same teams.

Late migration: triage what already broke

When the deadline has passed, the first job is an inventory of silent failures. Nothing at checkout throws a visible error when a legacy customisation stops running; the customisation simply is not there any more.

  1. Conversion tracking. Anything in the additional scripts box or in checkout.liquid on the thank-you page stopped firing at the store’s sunset date. Compare ad-platform conversion counts against Shopify orders for the weeks either side of the date. A step change is the fingerprint.
  2. Discount, shipping and payment logic. Ruby Scripts that hid payment methods, renamed shipping rates or applied tiered discounts stopped on 30 June 2026. Check orders after that date for discounts that should have applied and did not, or payment methods that should have been hidden.
  3. Post-purchase content. Survey widgets, delivery messaging and loyalty prompts on the Order status page that came from script tags are gone.
  4. App-provided features. Ask each checkout-adjacent app vendor whether they ship an extension or pixel. If they do, it usually needs placing in the checkout and accounts editor; installation alone does not put a block on the page.

To check programmatically whether a store has upgraded its post-purchase pages, query checkoutProfiles and read the typOspPagesActive field. Shopify recommends this query now that the configuration webhook has been removed.

Map each old customisation to its replacement

Old approach Replacement Notes
DOM edits or extra fields in checkout.liquid Checkout UI extension at a block or static target Information, shipping and payment step targets are Plus only
CSS overrides of checkout Checkout branding (editor or Admin API) Extensions cannot inject CSS
Additional scripts for pixels and conversions Web pixels (custom pixel or app pixel) Sandboxed, respects customer privacy settings
Ruby line-item or order discounts Discount Function One Discount Function API now covers product, order and shipping discounts
Ruby shipping and payment scripts Delivery and payment customisation Functions Hide, rename or reorder options
JavaScript checkout validation Cart and Checkout Validation Function Runs server-side, so it cannot be bypassed in the browser
Order status page script tags Customer account UI extension Only renders on upgraded stores
Upsell before the thank-you page Post-purchase extension Still beta; live stores need access approval

Checkout UI extensions in their current form

If you last looked at checkout UI extensions in 2024, the programming model has changed. From API version 2025-10, extensions use Preact and Polaris web components (<s-banner>, <s-checkbox>) and a global shopify object instead of the React hooks package. Version 2025-07 was the last to support React components.

The constraints are the point of the design, not an inconvenience to work around:

  • Extensions run in a Web Worker. There is no window, no access to the checkout DOM and no arbitrary HTML.
  • The compiled bundle cannot exceed 64 KB. Shopify enforces this at deploy time.
  • Calling your own backend requires the network_access capability, and reading buyer details is subject to protected customer data review.
  • As of API 2026-04, checkout metafields are removed. Use cart metafields in checkout extensions, which can be copied to order metafields when the order is created.

A minimal block that records a buyer choice as a cart metafield, following the pattern in Shopify’s migration guide:

# shopify.extension.toml  (use the latest stable API version your CLI offers)
api_version = "2026-07"

[[extensions]]
type = "ui_extension"
name = "Gift note"
handle = "gift-note"

  [[extensions.targeting]]
  target = "purchase.checkout.block.render"
  module = "./src/Checkout.jsx"
// src/Checkout.jsx
import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
  render(<GiftOption />, document.body);
}

function GiftOption() {
  async function onChange(event) {
    // Check the cart instructions before mutating: some checkouts disallow it.
    if (!shopify.instructions.value.metafields.canSetCartMetafields) return;

    await shopify.applyMetafieldChange({
      type: 'updateCartMetafield',
      metafield: {
        namespace: '$app:preferences',
        key: 'gift-wrap',
        type: 'single_line_text_field',
        value: event.target.checked ? 'yes' : 'no',
      },
    });
  }

  return <s-checkbox label="Gift wrap this order" onChange={onChange} />;
}

Two habits from Shopify’s performance guidance are worth adopting from the start: apply changes only once the buyer’s intent is clear (debounce text input), and batch several changes with Promise.all. Extensions that make excessive changes are rate limited for the rest of the session.

Shopify Functions: where the business logic goes

Functions are the replacement for Ruby Scripts, and they are a different kind of code. A Function is compiled to WebAssembly, receives a JSON input shaped by a GraphQL input query you write, and returns a list of operations. It cannot make arbitrary network calls (a few APIs support controlled network access), cannot use randomness or the clock, and runs within fixed resource limits: a 256 kB compiled binary, 10,000 kB of linear memory and a capped instruction count. Shopify recommends Rust over JavaScript for anything that has to handle large carts.

A Cart and Checkout Validation Function in JavaScript, blocking checkout when a cart attribute is missing:

// src/cart_validations_generate_run.js
// @ts-check
/**
 * @param {import('../generated/api').CartValidationsGenerateRunInput} input
 * @returns {import('../generated/api').CartValidationsGenerateRunResult}
 */
export function cartValidationsGenerateRun(input) {
  const step = input.buyerJourney.step;
  // Only enforce once the buyer is actually checking out.
  if (step !== 'CHECKOUT_INTERACTION' && step !== 'CHECKOUT_COMPLETION') {
    return {operations: []};
  }

  // purchaseOrder is an alias in the GraphQL input query: purchaseOrder: attribute(key: "po_number") { value }
  if (input.cart.purchaseOrder?.value) {
    return {operations: []};
  }

  return {
    operations: [{
      validationAdd: {
        errors: [{message: 'Enter a purchase order number to continue.', target: '$.cart'}],
      },
    }],
  };
}

Two platform rules shape the architecture. Custom apps containing Functions only run on Plus stores; non-Plus merchants need a public app. And because a Function cannot fetch data at run time in most APIs, anything it needs (tier thresholds, blocked SKUs, B2B rules) has to be pre-loaded into metafields or app-owned metaobjects that the input query reads. Plan that data model before porting the Ruby logic line by line. The app-side plumbing for this (OAuth, scopes, webhooks for keeping those metafields current) is covered in Shopify App Development: OAuth, Webhooks and Rate Limits.

Thank you, Order status and post-purchase

These three surfaces are separate and easily confused:

  • Thank you page: checkout UI extension targets prefixed purchase.thank-you.. Available on all plans except Shopify Starter. Can read metafields but cannot write them.
  • Order status page: now part of customer accounts, so it uses customer account UI extension targets prefixed customer-account.order-status.. An extension that should appear on both pages declares both targets.
  • Post-purchase page: the upsell page between payment and the thank-you page. Post-purchase extensions are still in beta; they work without restriction on development stores, but a live store needs approved access. They only render for Online Store orders, a buyer can accept at most three offers, and orders without a shipping address cannot receive subscription offers.

For tracking, do not try to rebuild thank-you page scripts as UI extensions. Use web pixels, which subscribe to standard events such as checkout_completed and run sandboxed with consent applied.

A migration checklist that holds up after the deadline

  • Pull the Shopify Scripts customisations report and the Thank you / Order status upgrade report in Settings > Checkout, and list every customisation they show.
  • Classify each item: UI (extension), logic (Function), tracking (pixel), styling (branding), or retire it.
  • Rebuild tracking first. It is the most expensive loss and the easiest to verify against order counts.
  • Port Ruby Scripts to Functions with the data they need held in metafields, and test against the largest real carts you can reproduce.
  • Upgrade any existing extension still on API 2025-07 or earlier to Preact and Polaris web components so it can keep shipping updates.
  • Replace checkout metafields with cart metafields when you move to API 2026-04 or later.
  • Test in a development store, then check the checkout and accounts editor to confirm each block is actually placed.

Checkout remains a bounded surface even on Plus, which is the platform-fit question discussed in E-Commerce Engineering: Shopify Custom Apps & Scalable WooCommerce Systems. If you are considering a headless storefront to gain checkout control, read Shopify Hydrogen and Custom Storefronts first: headless does not change who owns checkout.

Questions people actually ask

Does checkout.liquid still work for anyone?
No. It has been unsupported on the Information, Shipping and Payment steps since 2024, and it was sunset on the Thank you and Order status pages in August 2025. Non-Plus stores lost additional scripts and order-status script tags on 26 August 2026 and were upgraded automatically.

What happened to my additional scripts after the automatic upgrade?
They stopped running. The code is not migrated for you. Tracking needs rebuilding as web pixels or through each ad platform’s Shopify app, and any visible content needs a Thank you or Order status page extension.

Can non-Plus stores use checkout UI extensions?
On the Thank you and Order status pages, yes (except Shopify Starter). Extensions on the Information, Shipping and Payment steps are Plus only, as are custom apps that contain Shopify Functions.

My Shopify Scripts stopped in June. What is the fastest replacement?
Check the Scripts customisations report first: some common scripts map to native discount settings or existing public apps. Write a custom Function only for logic that nothing else covers.

Missed the checkout deadline?

I can audit what stopped working at your checkout and rebuild it as extensions, Functions and pixels, starting with the tracking and discount logic that cost revenue.

Book a checkout migration review →
← Back to all insights