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

658 lines
18 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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'
---
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',
() => <Extension />,
);
function Extension() {
return (
<AdminBlock title="Order status">
<BlockStack gap>
<TextField label="Tracking number" />
<Button title="Update" variant="primary" />
</BlockStack>
</AdminBlock>
);
}
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(<Extension />, document.body);
}
function Extension() {
// Data for this target comes through the global `shopify` object
const orderId = shopify.data.selected?.[0]?.id;
return (
<s-admin-block heading="Order summary">
<s-stack gap="base">
<s-text>Order ID: {orderId}</s-text>
<s-button variant="primary">Do something</s-button>
</s-stack>
</s-admin-block>
);
}
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(<Extension />, document.body).
UI is declared with Polaris web components:
<s-admin-block> (replaces AdminBlock, uses heading instead of title)
<s-stack> (replaces BlockStack)
<s-text>, <s-button>, 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](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"
}
}
```
***
## 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
```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 |
***