Apps & Extensions

Shopify Checkout UI Extensions: Migrate to Preact and Web Components

By CartStack Team6 min read

To migrate Shopify checkout UI extensions, you now have to move from the legacy React and Remote UI model to Preact and Polaris web components on a current API version. Shopify’s own engineering team says deployments that include older extension versions are blocked from October 1, 2026, a date that has now passed. This guide covers what to change, the 64KB bundle budget and the problems Shopify hit in its own migration. Checked against Shopify’s migration guide, the Checkout Blocks engineering write-up and a Shopify staff reply.

Cover graphic for migrating Shopify checkout UI extensions to Preact and web components
Plan the migration around the bundle budget and the deploy rules, not just the syntax.

What Changed for Checkout UI Extensions

Extensions on API version 2025-10 and later use Polaris web components by default. If your extension still targets an older version, Shopify’s guide says you need to adopt web components as part of the upgrade. In practice that means custom s-* elements instead of React components, a global shopify object instead of callback parameters, and Preact instead of React.

  • Unmigrated extensions keep running. A Shopify staff member says extensions that are not migrated keep working for merchants, but you cannot deploy them anymore.
  • One old extension can block the app. Per the same reply, every UI extension in an app must be on a supported API version before the app can be deployed.
  • The date. Shopify’s engineering post says deployments that include extension versions earlier than 2026-01 are blocked starting October 1, 2026, and that 2025-07 is being sunset.

One detail does not line up. The changelog quoted in the community thread names API versions 2025-07 or earlier, while the engineering post says earlier than 2026-01. Rather than guess which wording applies to you, move every extension to a current version. The migration guide itself uses 2026-04.

Step 1: Update the API Version and the CLI

Set the version in each extension’s shopify.extension.toml and upgrade the CLI.

api_version = "2026-04"
npm install -g @shopify/cli

Step 2: Move Metafields to appMetafields

As of 2026-04 the metafields property is deprecated. The guide gives these replacements.

  • Read values through shopify.appMetafields instead of shopify.metafields, useMetafield() or useMetafields().
  • Declare every metafield your extension needs with [[extensions.metafields]] in the TOML file.
  • Write cart metafields with the updateCartMetafield and removeCartMetafield change types. useApplyMetafieldsChange(), MetafieldUpdateChange and MetafieldRemoveChange are deprecated.
  • To copy cart metafields onto orders, the app needs the write_orders scope and a matching order metafield definition with the cart_to_order_copyable capability.

Metafield declarations are also about to get stricter. See our guide to the owner_type requirement coming in API 2027-01, and our explainer on metafields vs metaobjects if the data model itself needs a second look.

Step 3: Adopt Preact and Web Components

This step applies when you upgrade from a version earlier than 2025-10. The guide lists these changes.

  • Dependencies: use preact (^10.10.x), @preact/signals (^2.3.x) and @shopify/ui-extensions (2026.4.x), and remove the React packages.
  • TypeScript: add an extension-level tsconfig.json with jsx set to react-jsx, jsxImportSource set to preact, and include covering ./src and ./shopify.d.ts.
  • Types: run shopify app dev. It generates shopify.d.ts in the extension directory and adds the global shopify object.
  • Entry point: import @shopify/ui-extensions/preact and render your component into document.body from an exported function.
  • APIs: call them on the global object, for example await shopify.applyAttributeChange(...).
  • Hooks: import hooks such as useAttributeValues from @shopify/ui-extensions/checkout/preact, or prefer the global object for simple reads and writes.
  • Components: replace React components with custom elements such as <s-stack>, <s-text-field>, <s-button> and <s-checkbox>. The guide links a per-component upgrade page for roughly 60 legacy components.

The sketch below shows the shape of an entry point. It is our illustration of the pattern the guide describes, so compare it with the template that shopify app generate extension gives you before relying on it.

import '@shopify/ui-extensions/preact';
import { render } from 'preact';

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

function Extension() {
  return (
    <s-stack gap="base">
      <s-text>Hello from a web component extension</s-text>
    </s-stack>
  );
}

Stay Under the 64KB Bundle Budget

Shopify’s engineering post says the 2026-01 remote-dom CLI enforces a hard 64KB gzip limit per extension bundle. Their Checkout Blocks extensions started around 100KB to 112KB gzipped, so they had to cut roughly half. These are the changes that paid off.

  • Switching from React to Preact removed react-reconciler, saving about 89KB. This was the biggest win.
  • Replacing liquidjs with an in-house Liquid parser saved about 73KB raw, roughly 13KB gzipped against about 22KB.
  • Replacing dayjs with a small date utility saved about 12KB.
  • Aliasing React to Preact kept markdown-to-jsx working, because it uses no hooks and only imports createElement, Fragment and cloneElement.

Shopify reports transferred bundle sizes down 40% to 85% across the five extensions, and Extension Load Time down 8% at the median and 7% at the 90th percentile, weighted by checkout volume.

Problems Shopify Hit in Its Own Migration

  • Duplicate IDs. Preact’s useId() produced identical IDs for multiple instances of the same extension. That broke the commandFor and id pairing for web components, so overlays opened the wrong modal. It was their one production rollback. The fix was a hook that generates random, instance-unique IDs.
  • Text alignment. textAlign had been removed from Paragraph and Heading, which broke centered layouts.
  • Checkbox labels. s-checkbox accepted only a plain string label, which blocked inline links until a label slot was added upstream.

Our Analysis: How to Plan the Migration

Inventory first, then migrate. Because one old extension can block a whole app deploy, list every checkout and customer account extension across your apps before touching code. Our suggestion is to treat the oldest one as the critical path.

Measure bundle size on day one. A 64KB hard limit is a pass or fail check, not a tuning target. Build a trivial extension first and see what your dependencies cost, then decide which libraries to replace.

Ship one extension at a time. Shopify released at most one extension per day, lowest risk first, with success and failure metrics in place beforehand. That is a sound pattern for any team.

Use tooling, then review. Shopify published an AI toolkit and upgrade guides for this migration, and its own team used an agent for mechanical conversion. Our AI Toolkit vs Dev MCP comparison explains where that fits. Review the output, especially IDs, alignment and accessibility.

FAQ about Checkout UI Extension Migration

Will live extensions stop working for merchants? According to a Shopify staff reply, unmigrated extensions keep working for merchants, but you cannot deploy new versions.

Which API version should I target? Shopify’s guide uses 2026-04. Versions 2025-10 and later already use web components by default.

Do I have to use Preact? For upgrades from before 2025-10, the guide tells you to adopt Preact and web components together.

Does this affect POS extensions? The sources we read cover checkout and customer account UI extensions. We could not confirm from them whether POS extensions follow the same date, so check Shopify’s documentation for your extension type.