first commit

This commit is contained in:
O K
2026-05-20 17:32:11 +03:00
commit c92aee86a2
33 changed files with 11584 additions and 0 deletions

658
upgradingUI.md Normal file
View File

@@ -0,0 +1,658 @@
---
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 |
***