--- title: Upgrade to the latest API version and Polaris web components description: >- Learn how to upgrade your checkout UI extensions to use the latest API version and Polaris web components. source_url: html: 'https://shopify.dev/docs/apps/build/checkout/migrate-to-web-components' md: 'https://shopify.dev/docs/apps/build/checkout/migrate-to-web-components.md' --- # Upgrade to the latest API version and Polaris web components This guide explains the steps you need to take to migrate your checkout UI extensions to the latest API version, including metafield APIs. **Info:** If you're upgrading from a version earlier than `2025-10`, you'll need to adopt Polaris web components. Versions `2025-10` and later use web components by default. []() *** ## Step 1: Update API version Update the API version to the latest version in `shopify.extension.toml` (for example, `2026-04`). ## shopify.extension.toml ```toml api_version = "2026-04" [[extensions]] name = "your-extension" handle = "your-extension" type = "ui_extension" # Contents of your existing file... ``` []() *** ## Step 2: Migrate to cart metafields As of the 2026-04 API version, the `metafields` property is deprecated. Use `shopify.appMetafields` to access values that you previously read using `shopify.metafields` or the `useMetafield()` and `useMetafields()` hooks. Use cart metafields and update them using `MetafieldUpdateCartChange` and `MetafieldRemoveCartChange`. The `useApplyMetafieldsChange()` hook is deprecated, as well as the `MetafieldRemoveChange` and the `MetafieldUpdateChange` inputs to the `applyMetafieldChange` method. See [metafields API](https://shopify.dev/docs/api/checkout-ui-extensions/latest/target-apis/platform-apis/metafields-api) for more details. **Info:** To persist cart metafields to orders, ensure your app has the `write_orders` [scope](https://shopify.dev/docs/api/usage/access-scopes) and create a matching order metafield definition with the [`cart_to_order_copyable`](https://shopify.dev/docs/apps/build/metafields/use-metafield-capabilities#cart-to-order-copyable) capability. For a step-by-step walkthrough, see the [add a field to checkout](https://shopify.dev/docs/apps/build/checkout/fields-banners/add-field) tutorial. ### Request metafields in your extension TOML Request metafields that your extension needs access to in your extension configuration using `[[extensions.metafields]]`. See [configurations](https://shopify.dev/docs/apps/build/app-extensions/configure-app-extensions#checkout-ui-extensions) for more details. ## shopify.extension.toml ```toml [[extensions.metafields]] namespace = "my-namespace" key = "gift-requested" ``` ### Update API calls Use `shopify.appMetafields` to read cart metafields. To write data, use cart metafield change types `updateCartMetafield` and `removeCartMetafield`. #### Before: Checkout metafields (now removed) ## package.json ## tsx ```tsx import "@shopify/ui-extensions/preact"; import { render } from "preact"; import { useMetafield } from "@shopify/ui-extensions/checkout/preact"; export default function extension() { render(, document.body); } function Extension() { const giftRequested = useMetafield({ namespace: "my-namespace", key: "gift-requested", }); return ( ); async function onCheckboxChange(event) { const isChecked = event.target.checked; if (isChecked) { await shopify.applyMetafieldChange({ type: "updateMetafield", namespace: "my-namespace", key: "gift-requested", valueType: "string", value: "true", }); } else { await shopify.applyMetafieldChange({ type: "removeMetafield", namespace: "my-namespace", key: "gift-requested", }); } } } ``` #### After: App metafields and cart metafield change types ## package.json ## tsx ```tsx import "@shopify/ui-extensions/preact"; import { render } from "preact"; export default async () => { render(, document.body); }; function Extension() { const giftRequested = shopify.appMetafields.value.find( (appMetafield) => appMetafield.target.type === "cart" && appMetafield.metafield.namespace === "my-namespace" && appMetafield.metafield.key === "gift-requested", ); return ( ); async function onCheckboxChange(event) { const isChecked = event.target.checked; if (isChecked) { await shopify.applyMetafieldChange({ type: "updateCartMetafield", metafield: { namespace: "my-namespace", key: "gift-requested", value: "true", type: "boolean", }, }); } else { await shopify.applyMetafieldChange({ type: "removeCartMetafield", namespace: "my-namespace", key: "gift-requested", }); } } } ``` []() *** ## Step 3: Adopt web components and Preact The following sections apply only if you're upgrading from a version earlier than `2025-10`. If you're already using web components and Preact, you don't need to make any changes. ### Adjust package dependencies For API versions `2025-10` and later, Shopify recommends Preact for UI extensions. Update your `package.json` file to specify the latest dependencies and re-install them. #### New dependencies with Preact ## package.json ## New dependencies with Preact ```json { "dependencies": { "preact": "^10.10.x", "@preact/signals": "^2.3.x", "@shopify/ui-extensions": "2026.4.x" } } ``` #### Previous dependencies with React ## package.json ## Previous dependencies with React ```json { "dependencies": { "react": "^18.0.0", "@shopify/ui-extensions": "2025.4.x", "@shopify/ui-extensions-react": "2025.4.x", "react-reconciler": "0.29.0" }, "devDependencies": { "@types/react": "^18.0.0" } } ``` #### Previous dependencies with JavaScript ## package.json ## Previous dependencies with JavaScript ```json { "dependencies": { "@shopify/ui-extensions": "2025.4.x" } } ``` ### Type​Script configuration Get full IntelliSense and auto-complete support by adding a config file for your extension at `extensions/{extension-name}/tsconfig.json`. You don't need to change your app's root `tsconfig.json` file. #### New tsconfig.json ## tsconfig.json ## New tsconfig.json ```json { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact", "target": "ES2020", "checkJs": true, "allowJs": true, "moduleResolution": "node", "esModuleInterop": true }, "include": ["./src", "./shopify.d.ts"] } ``` #### Old tsconfig.json ## tsconfig.json ## Old tsconfig.json ```json { "compilerOptions": { "jsx": "react-jsx" }, "include": ["./src"] } ``` ### Upgrade the Shopify CLI The new [Shopify CLI](https://shopify.dev/docs/api/shopify-cli) adds support for building with web components. The `shopify app dev` command runs your app and also generates a `shopify.d.ts` file in your extension directory, adding support for the new global `shopify` object. ## Support new global shopify object ## CLI ```bash # Upgrade to latest version of the CLI npm install -g @shopify/cli # Run the app to generate the type definition file shopify app dev ``` ### Optional ESLint configuration If your app is using ESLint, update your configuration to include the new global `shopify` object. ## .eslintrc.cjs ```js module.exports = { globals: { shopify: 'readonly', }, }; ``` ### Migrate API calls Instead of accessing APIs from a callback parameter, access them from the global `shopify` object. Here's an example of migrating the `applyAttributeChange` API call. #### New API calls in Preact ## Preact ## New API calls in Preact ```tsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; export default function extension() { render(, document.body); } function Extension() { return ( ); } async function onCheckboxChange(event) { const isChecked = event.target.checked; const result = await shopify.applyAttributeChange({ type: 'updateAttribute', key: 'includeGift', value: isChecked ? 'yes' : 'no', }); console.log( 'applyAttributeChange result', result, ); } ``` #### Previous API calls in React ## React ## Previous API calls in React ```tsx import { reactExtension, Checkbox, useApi, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => , ); function Extension() { const api = useApi(); async function onCheckboxChange(isChecked) { const result = await api.applyAttributeChange( { type: 'updateAttribute', key: 'includeGift', value: isChecked ? 'yes' : 'no', }, ); console.log( 'applyAttributeChange result', result, ); } return ( Include a complimentary gift ); } ``` #### Previous API calls in JavaScript ## JavaScript ## Previous API calls in JavaScript ```ts import { extension, Checkbox, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, api) => { async function onCheckboxChange(isChecked) { const result = await api.applyAttributeChange({ type: 'updateAttribute', key: 'includeGift', value: isChecked ? 'yes' : 'no', }); console.log( 'applyAttributeChange result', result, ); } root.replaceChildren( root.createComponent( Checkbox, { onChange: onCheckboxChange, }, 'Include a complimentary gift', ), ); }, ); ``` ### Migrate hooks If you had previously been using React hooks, you can continue using them by importing those same hooks from a new Preact-specific package. Here's an example of migrating the `useAttributeValues` hook: #### New hooks in Preact ## Preact ## New hooks in Preact ```tsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import {useAttributeValues} from '@shopify/ui-extensions/checkout/preact'; export default function extension() { render(, document.body); } function Extension() { const [includeGift] = useAttributeValues([ 'includeGift', ]); return ( ); } async function onCheckboxChange(event) { const isChecked = event.target.checked; const result = await shopify.applyAttributeChange({ type: 'updateAttribute', key: 'includeGift', value: isChecked ? 'yes' : 'no', }); console.log( 'applyAttributeChange result', result, ); } ``` #### Previous hooks in React ## React ## Previous hooks in React ```tsx import { reactExtension, Checkbox, useAttributeValues, useApplyAttributeChange, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => , ); function Extension() { const [includeGift] = useAttributeValues([ 'includeGift', ]); const applyAttributeChange = useApplyAttributeChange(); async function onCheckboxChange(isChecked) { const result = await applyAttributeChange({ type: 'updateAttribute', key: 'includeGift', value: isChecked ? 'yes' : 'no', }); console.log( 'applyAttributeChange result', result, ); } return ( Include a complimentary gift ); } ``` **Info:** For hooks that perform simple read or write operations, we recommend that you use the [`shopify` global object](https://shopify.dev/docs/api/checkout-ui-extensions/%7BAPI_VERSION%7D#target-apis-define-what-your-extension-does) instead. Some Preact hooks take parameters and are provided as convenience wrappers to make it easier to perform common operations without writing your own logic. These hooks are documented on the individual API reference pages in [Target APIs](https://shopify.dev/docs/api/checkout-ui-extensions/latest/target-apis). ### Migrate to web components [Web components](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components) are exposed as custom HTML elements. Update your React or JavaScript components to custom elements. #### New components in Preact ## Preact ## New components in Preact ```tsx /* eslint-disable react/self-closing-comp */ import '@shopify/ui-extensions/preact'; import {render} from 'preact'; export default function extension() { render(, document.body); } function Extension() { return ( Save ); } ``` #### Previous components in React ## React ## Previous components in React ```tsx import { reactExtension, InlineStack, TextField, Button, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.block.render', () => , ); function Extension() { return ( ); } ``` #### Previous components in JavaScript ## JavaScript ## Previous components in JavaScript ```ts import { extension, InlineStack, TextField, Button, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.block.render', (root, _api) => { root.replaceChildren( root.createComponent(InlineStack, {}, [ root.createComponent(TextField, { label: 'Gift message', }), root.createComponent(Button, {}, 'Save'), ]), ); }, ); ``` ### Mapping legacy components to web components | **Legacy Component** | **Web Component** | **Migration Notes** | | - | - | - | | | [Abbreviation](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/abbreviation) | New component | | `Badge` | [Badge](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/feedback-and-status-indicators/badge) | | | `Banner` | [Banner](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/feedback-and-status-indicators/banner) | | | `BlockLayout` | | Removed. Use [Grid](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/grid) | | `BlockSpacer` | | Removed. Use [Stack](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/stack) with `gap` property | | `BlockStack` | | Removed. Use [Stack](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/stack) with `direction=block` | | `Button` | [Button](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/actions/button) | | | `Checkbox` | [Checkbox](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/checkbox) | | | `Choice` | [Choice](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/choice-list#choice) | | | `ChoiceList` | [ChoiceList](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/choice-list) | | | `ClipboardItem` | [ClipboardItem](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/actions/clipboard-item) | | | `ConsentCheckbox` | [ConsentCheckbox](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/consent-checkbox) | | | `ConsentPhoneField` | [ConsentPhoneField](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/consent-phone-field) | | | `DateField` | [DateField](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/date-field) | | | `DatePicker` | [DatePicker](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/date-picker) | | | `Disclosure` | [Details](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/details) and [Summary](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/details#summary) | | | `Divider` | [Divider](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/divider) | | | `DropZone` | [DropZone](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/drop-zone) | | | | [EmailField](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/email-field) | New component | | `Form` | [Form](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/form) | | | `Grid` | [Grid](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/grid) | | | `GridItem` | [GridItem](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/grid#griditem) | | | `Heading` | [Heading](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/heading) | | | `HeadingGroup` | [Section](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/section) | | | `Icon` | [Icon](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/media-and-visuals/icon) | | | `Image` | [Image](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/media-and-visuals/image) | | | `InlineLayout` | | Removed. Use [Grid](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/grid) | | `InlineSpacer` | | Removed. Use [Stack](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/stack) | | `InlineStack` | | Removed. Use [Stack](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/stack) with `direction=inline` | | `Link` | [Link](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/actions/link) | | | `List` | [UnorderedList](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/unordered-list) or [OrderedList](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/ordered-list) | | | `ListItem` | ListItem | Use inside [UnorderedList](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/unordered-list) or [OrderedList](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/ordered-list) | | `Map` | [Map](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/media-and-visuals/map) | | | `MapMarker` | [MapMarker](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/media-and-visuals/map#mapmarker) | | | `MapPopover` | [Popover](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/overlays/popover) | | | `Modal` | [Modal](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/overlays/modal) | | | | [MoneyField](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/money-field) | New component | | `PaymentIcon` | [PaymentIcon](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/media-and-visuals/payment-icon) | | | `PhoneField` | [PhoneField](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/phone-field) | | | `Popover` | [Popover](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/overlays/popover) | | | `Pressable` | [Clickable](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/actions/clickable) | | | `ProductThumbnail` | [ProductThumbnail](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/media-and-visuals/product-thumbnail) | | | `Progress` | [Progress](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/feedback-and-status-indicators/progress) | | | `QRCode` | [QRCode](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/media-and-visuals/qr-code) | | | `ScrollView` | [ScrollBox](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/scroll-box) | | | `Select` | [Select](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/select) | | | `Sheet` | [Sheet](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/overlays/sheet) | | | `SkeletonImage` | | Removed | | `SkeletonText` | [SkeletonParagraph](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/skeleton-paragraph) | | | `SkeletonTextBlock` | [SkeletonParagraph](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/skeleton-paragraph) | | | `Spinner` | [Spinner](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/feedback-and-status-indicators/spinner) | | | `Stepper` | [NumberField](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/number-field) | | | `Switch` | [Switch](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/switch) | | | `Tag` | [Chip](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/chip) and [ClickableChip](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/actions/clickable-chip) | | | `Text` | [Text](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/text) | | | `TextField` with `multiline` | [TextArea](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/text-area) | | | `TextBlock` | [Paragraph](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/paragraph) | | | `TextField` | [TextField](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/text-field) | | | | [Time](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/typography-and-content/time) | New component | | `ToggleButton` | [PressButton](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/actions/press-button) | | | `ToggleButtonGroup` | [ChoiceList](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/forms/choice-list) or [PressButton](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/actions/press-button) | | | `Tooltip` | [Tooltip](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/overlays/tooltip) | | | `View` | [Box](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/web-components/layout-and-structure/box) | | ***