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

# Upgrading to 2025-10

This guide describes how to upgrade your admin UI extension to API version `2025-10` and adopt [web components](https://shopify.dev/docs/api/admin-extensions/latest/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

```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

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

## Previous dependencies with React

## package.json

```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

```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

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

## Old tsconfig.json

```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

```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 uses 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 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

```tsx
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

```tsx
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

```ts
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

```tsx
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

```tsx
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

```tsx
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

```tsx
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

```ts
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](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components) equivalent.

| **Legacy component** | **Web component** | **Migration notes** |
| - | - | - |
| `AdminAction` | [`Admin action`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-action) | Use `heading` instead of `title`. Primary and secondary actions are rendered as child elements. |
| `AdminBlock` | [`Admin block`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-block) | Use `heading` instead of `title`. |
| `AdminPrintAction` | [`Admin print action`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-print-action) | None |
| `Badge` | [`Badge`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/badge) | None |
| `Banner` | [`Banner`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/banner) | None |
| `BlockStack` | [`Stack`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/stack) | Use the stack component with default block direction. |
| `InlineStack` | [`Stack`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/stack) | Use the stack component with `direction="inline"`. |
| `Box` | [`Box`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/box) | None |
| `Button` | [`Button`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button) | None |
| `Checkbox` | [`Checkbox`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/checkbox) | None |
| `ChoiceList` | [`Choice list`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/choice-list) | None |
| `ColorPicker` | [`Color picker`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-picker) | None |
| `CustomerSegmentTemplate` | [Customer Segment Template Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/contextual-apis/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`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-field) | None |
| `DatePicker` | [`Date picker`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-picker) | None |
| `Divider` | [`Divider`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/divider) | None |
| `EmailField` | [`Email field`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/email-field) | None |
| `Form` | [`Form`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/form) | None |
| `FunctionSettings` | [`Function settings`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/function-settings) | None |
| `Heading` | [`Heading`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/heading) | None |
| `HeadingGroup` | | Removed. Use heading levels directly. |
| `Icon` | [`Icon`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/icon) | None |
| `Image` | [`Image`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/image) | None |
| `Link` | [`Link`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/link) | None |
| `MoneyField` | [`Money field`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/money-field) | None |
| `NumberField` | [`Number field`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/number-field) | None |
| `Paragraph` | [`Paragraph`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/paragraph) | None |
| `PasswordField` | [`Password field`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/password-field) | None |
| `Pressable` | [`Clickable`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable) | None |
| `ProgressIndicator` | [`Spinner`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/spinner) | None |
| `Section` | [`Section`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/section) | None |
| `Select` | [`Select`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/select) | None |
| `Text` | [`Text`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/text) | None |
| `TextArea` | [`Text area`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-area) | None |
| `TextField` | [`Text field`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-field) | None |
| `UrlField` | [`URL field`](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/url-field) | None |

***
