Files
shopify_proxy/upgradingUI.md
2026-05-20 17:32:11 +03:00

18 KiB
Raw Blame History

title, description, source_url
title description source_url
Upgrading to 2025-10 This guide describes how to upgrade your admin UI extension to API version `2025-10` and adopt web components.
html md
https://shopify.dev/docs/apps/build/admin/upgrading-to-2025-10 https://shopify.dev/docs/apps/build/admin/upgrading-to-2025-10.md

For a new admin order UI block (for example, admin.order-details.block.render), you should use Preact + Polaris web components, not React.

The current guidance in the admin UI extensions docs is:

Version 2025-07 is the last API version that supports React-based UI components. From 2025-10 and onward, Shopify recommends Preact and web components for admin UI extensions. See: Upgrading to 2025-10 (admin UI extensions, Preact & web components) Why Preact (web components) over React? From the upgrade guide for admin UI extensions (Upgrading to 2025-10):

React-based @shopify/ui-extensions-react components are being phased out. “Version 2025-07 is the last API version to support React-based UI components. Web components replace them…” The recommended stack going forward is: preact @preact/signals (optional but nice for reactive state) @shopify/ui-extensions (web components + APIs) New admin UI extensions (including order blocks) are scaffolded and documented with Preact by default. So:

If youre starting anything new today (including admin order UI blocks): Use Preact + <s-*> web components. If you already have legacy React-based admin UI extensions: You can stay on an older API version (e.g. 2025-04/2025-07) short-term, But youll eventually need to upgrade to 2025-10 and migrate those to Preact + web components. What this looks like for admin.order-details.block.render In React (legacy), an order details block might have looked like this (from the upgrade guide):

tsx Copy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // LEGACY React-based admin block (2025-07 or earlier) import { reactExtension, AdminBlock, BlockStack, TextField, Button, } from '@shopify/ui-extensions-react/admin';

export default reactExtension( 'admin.order-details.block.render', () => , );

function Extension() { return ( ); } For new work you should instead do something like:

tsx Copy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import '@shopify/ui-extensions/preact'; import {render} from 'preact';

export default function extension() { render(, document.body); }

function Extension() { // Data for this target comes through the global shopify object const orderId = shopify.data.selected?.[0]?.id;

return ( Order ID: {orderId} Do something ); } This Preact/web-components example has been validated against the polaris-admin-extensions API for the admin.order-details.block.render target.

Key points:

Use import '@shopify/ui-extensions/preact'; and render from preact. The entry export is a function that calls render(, document.body). UI is declared with Polaris web components: (replaces AdminBlock, uses heading instead of title) (replaces BlockStack) , , etc. Data comes from the global shopify object, e.g. shopify.data.selected?.[0]?.id for the selected order. When might you still use React? Youd only stick with React if:

You have existing React-based admin UI extensions already in production. You need a short-term, low-risk fix and dont want to migrate immediately. Youre okay staying on an older API version (≤ 2025-07) for a while. Even in that case, Shopifys docs explicitly push you toward:

Upgrading API version to 2025-10 in shopify.extension.toml. Adjusting your package.json dependencies to use Preact instead of React. See the “Adjust package dependencies” section in Upgrading to 2025-10. Practical recommendation for your admin order block For an admin order UI block (e.g. admin.order-details.block.render):

Scaffold the extension with the CLI (as recommended): bash Copy 1 shopify app generate extension --template admin_block --name my-admin-order-block In the generated extension:

Ensure api_version in shopify.extension.toml is set to 2025-10 (or later). Use Preact + web components in your Extension.jsx/tsx. Only consider React if:

Youre locked to an older API version for now, and You plan a future migration to Preact/web components.

Upgrading to 2025-10

This guide describes how to upgrade your admin UI extension to API version 2025-10 and adopt web components from Polaris, Shopify's unified system for building app interfaces.

Version 2025-07 is the last API version to support React-based UI components. Web components replace them with native UI elements that offer built-in accessibility, better performance, and consistent styling — so your extension looks and behaves like the rest of the Shopify admin.


Update API version

Set the API version to 2025-10 in shopify.extension.toml to use web components.

shopify.extension.toml

api_version = "2025-10"


[[extensions]]
name = "your-extension"
handle = "your-extension"
type = "ui_extension"
uid = "ab22fe63-a741-cbc6-90c1-fbcf94a84426b9cbbe1f"


# Contents of your existing file...

Adjust package dependencies

As of 2025-10, Shopify recommends Preact for UI extensions. Update the dependencies in your package.json file and re-install.

New dependencies with Preact

package.json

{
  "dependencies": {
    "preact": "^10.10.x",
    "@preact/signals": "^2.3.x",
    "@shopify/ui-extensions": "2025.10.x"
  }
}

Previous dependencies with React

package.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

{
  "dependencies": {
    "@shopify/ui-extensions": "2025.4.x"
  }
}

TypeScript 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

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact",
    "target": "ES2020",
    "checkJs": true,
    "allowJs": true,
    "moduleResolution": "node",
    "esModuleInterop": true
  }
}

Old tsconfig.json

{
  "compilerOptions": {
    "jsx": "react-jsx"
  },
  "include": ["./src"]
}

Upgrade the Shopify CLI

The new CLI adds support for building 2025-10 extensions.

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

# 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 uses ESLint, update your configuration to include the new global shopify object.

.eslintrc.cjs

module.exports = {
  globals: {
    shopify: 'readonly',
  },
};

Migrate API calls

Instead of accessing APIs from a callback parameter or React hook, access them from the global shopify object. Here's an example of migrating API calls for an admin block.

New API calls in Preact

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


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


function Extension() {
  const productId = shopify.data.selected?.[0]?.id;


  return (
    <s-admin-block heading="Product information">
      <s-text>Product ID: {productId}</s-text>
    </s-admin-block>
  );
}

Previous API calls in React

import {
  reactExtension,
  useApi,
  AdminBlock,
  Text,
} from '@shopify/ui-extensions-react/admin';


export default reactExtension(
  'admin.product-details.block.render',
  () => <Extension />,
);


function Extension() {
  const {data} = useApi();
  const productId = data.selected?.[0]?.id;


  return (
    <AdminBlock title="Product information">
      <Text>Product ID: {productId}</Text>
    </AdminBlock>
  );
}

Previous API calls in JavaScript

import {extension, AdminBlock, Text} from '@shopify/ui-extensions/admin';


export default extension(
  'admin.product-details.block.render',
  (root, api) => {
    const productId = api.data.selected?.[0]?.id;


    const adminBlock = root.createComponent(
      AdminBlock,
      {title: 'Product information'},
      [root.createComponent(Text, {}, `Product ID: ${productId}`)],
    );


    root.appendChild(adminBlock);
    root.mount();
  },
);

Migrate hooks

If you were previously using React hooks, import those same hooks from a Preact-specific package. Here's an example of migrating hooks for an admin action.

New hooks in Preact

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


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


function Extension() {
  const [title, setTitle] = useState('');


  async function handleSubmit() {
    const productId = shopify.data.selected?.[0]?.id;
    await fetch('shopify:admin/api/graphql.json', {
      method: 'POST',
      body: JSON.stringify({
        query: `mutation SetMetafield($input: [MetafieldsSetInput!]!) {
          metafieldsSet(metafields: $input) { metafields { id } }
        }`,
        variables: {
          input: [{
            ownerId: productId,
            namespace: 'my-app',
            key: 'title',
            type: 'single_line_text_field',
            value: title,
          }],
        },
      }),
    });
    shopify.close();
  }


  return (
    <s-admin-action heading="Set title">
      <s-text-field
        label="Title"
        value={title}
        onInput={(e) => setTitle(e.target.value)}
      />
      <s-button variant="primary" onClick={handleSubmit}>
        Save
      </s-button>
    </s-admin-action>
  );
}

Previous hooks in React

import React, {useState} from 'react';
import {
  reactExtension,
  useApi,
  AdminAction,
  Button,
  TextField,
} from '@shopify/ui-extensions-react/admin';


export default reactExtension(
  'admin.product-details.action.render',
  () => <Extension />,
);


function Extension() {
  const {close, data, query} = useApi();
  const [title, setTitle] = useState('');


  async function handleSubmit() {
    const productId = data.selected?.[0]?.id;
    await query(
      `mutation SetMetafield($input: [MetafieldsSetInput!]!) {
        metafieldsSet(metafields: $input) { metafields { id } }
      }`,
      {
        variables: {
          input: [{
            ownerId: productId,
            namespace: 'my-app',
            key: 'title',
            type: 'single_line_text_field',
            value: title,
          }],
        },
      },
    );
    close();
  }


  return (
    <AdminAction title="Set title" primaryAction={<Button onPress={handleSubmit}>Save</Button>}>
      <TextField label="Title" value={title} onChange={setTitle} />
    </AdminAction>
  );
}

Migrate to web components

Web components are exposed as custom HTML elements. Update your React components to custom elements.

New components in Preact

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


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


function Extension() {
  return (
    <s-admin-block heading="Order status">
      <s-stack gap="base">
        <s-text-field label="Tracking number"></s-text-field>
        <s-button variant="primary">Update</s-button>
      </s-stack>
    </s-admin-block>
  );
}

Previous components in React

import {
  reactExtension,
  AdminBlock,
  BlockStack,
  TextField,
  Button,
} from '@shopify/ui-extensions-react/admin';


export default reactExtension(
  'admin.order-details.block.render',
  () => <Extension />,
);


function Extension() {
  return (
    <AdminBlock title="Order status">
      <BlockStack gap>
        <TextField label="Tracking number" />
        <Button title="Update" variant="primary" />
      </BlockStack>
    </AdminBlock>
  );
}

Previous components in JavaScript

import {
  extension,
  AdminBlock,
  BlockStack,
  TextField,
  Button,
} from '@shopify/ui-extensions/admin';


export default extension(
  'admin.order-details.block.render',
  (root, _api) => {
    root.replaceChildren(
      root.createComponent(
        AdminBlock,
        {title: 'Order status'},
        [
          root.createComponent(BlockStack, {gap: true}, [
            root.createComponent(TextField, {label: 'Tracking number'}),
            root.createComponent(Button, {
              title: 'Update',
              variant: 'primary',
            }),
          ]),
        ],
      ),
    );
  },
);

Web components mapping

The following table maps each legacy React component to its web component equivalent.

Legacy component Web component Migration notes
AdminAction Admin action Use heading instead of title. Primary and secondary actions are rendered as child elements.
AdminBlock Admin block Use heading instead of title.
AdminPrintAction Admin print action None
Badge Badge None
Banner Banner None
BlockStack Stack Use the stack component with default block direction.
InlineStack Stack Use the stack component with direction="inline".
Box Box None
Button Button None
Checkbox Checkbox None
ChoiceList Choice list None
ColorPicker Color picker None
CustomerSegmentTemplate Customer Segment Template Extension API Replaced by a target API. Return template data from the admin.customers.segmentation-templates.data target instead of rendering a component.
DateField Date field None
DatePicker Date picker None
Divider Divider None
EmailField Email field None
Form Form None
FunctionSettings Function settings None
Heading Heading None
HeadingGroup Removed. Use heading levels directly.
Icon Icon None
Image Image None
Link Link None
MoneyField Money field None
NumberField Number field None
Paragraph Paragraph None
PasswordField Password field None
Pressable Clickable None
ProgressIndicator Spinner None
Section Section None
Select Select None
Text Text None
TextArea Text area None
TextField Text field None
UrlField URL field None