Migrate ResourceItem to the Polaris section component
The Polaris section component groups related content inside a bordered container with an optional heading and action slots. It replaces the previous ResourceItem component and is available as <s-section> in API versions 2025-10 and newer.
Migrating ResourceItem to s-section
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-section heading="Order #1001">
<s-text>Placed on April 12, 2026</s-text>
<s-button slot="primary-action" variant="primary">
Buy again
</s-button>
<s-button slot="secondary-actions">View details</s-button>
</s-section>
);
}Pre-Polaris (2025-07)
import {
Button,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem
action={
<>
<Button kind="primary">Buy again</Button>
<Button kind="secondary">View details</Button>
</>
}
>
<Text>Order #1001</Text>
<Text>Placed on April 12, 2026</Text>
</ResourceItem>
);
}Anchor to Updated propertiesUpdated properties
The following properties are different in the Polaris section component.
Anchor to actionaction
The previous ResourceItem action prop accepted a fragment of Button components that were rendered alongside the item's content. On the Polaris section, use the primary-action and secondary-actions slots instead. Assign a single button to primary-action and one or more buttons to secondary-actions using the slot attribute.
Migrating action to slots
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-section heading="Order #1001">
<s-text>Placed on April 12, 2026</s-text>
<s-button slot="primary-action" variant="primary">
Buy again
</s-button>
<s-button slot="secondary-actions">Track shipment</s-button>
<s-button slot="secondary-actions">Return</s-button>
</s-section>
);
}Pre-Polaris (2025-07)
import {
Button,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem
action={
<>
<Button kind="primary">Buy again</Button>
<Button kind="secondary">Track shipment</Button>
<Button kind="secondary">Return</Button>
</>
}
>
<Text>Order #1001</Text>
<Text>Placed on April 12, 2026</Text>
</ResourceItem>
);
}Buttons placed in the primary-action and secondary-actions slots use the updated <s-button> API. The following properties are different from the previous Button used inside action.
Anchor to kindkind
The previous Button kind prop is now variant. Use the following mapping:
Previous kind | New variant |
|---|---|
'primary' | 'primary' |
'secondary' | 'secondary' |
'plain' | Removed. |
Anchor to onPresson Press
The previous Button onPress prop is now called onClick.
The previous Button to prop is now called href.
Anchor to overlayoverlay
The previous Button overlay prop has been removed. To open a modal from a button in the primary-action or secondary-actions slot, use command and commandFor to target a sibling modal by ID.
Migrating overlay to command
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<>
<s-section heading="Order #1001">
<s-text>Placed on April 12, 2026</s-text>
<s-button
slot="primary-action"
variant="primary"
command="--show"
commandFor="order-details-modal"
>
View details
</s-button>
</s-section>
<s-modal id="order-details-modal" heading="Order details">
<s-text>Your order ships in 2 to 4 business days.</s-text>
</s-modal>
</>
);
}Pre-Polaris (2025-07)
import {
Button,
Modal,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem
action={
<Button
kind="primary"
overlay={
<Modal id="order-details-modal" title="Order details">
<Text>Your order ships in 2 to 4 business days.</Text>
</Modal>
}
>
View details
</Button>
}
>
<Text>Order #1001</Text>
<Text>Placed on April 12, 2026</Text>
</ResourceItem>
);
}Anchor to loadingLabelloading Label
The previous Button loadingLabel prop has been removed.
Anchor to New propertiesNew properties
The Polaris section component introduces the following new properties:
| New prop | Type | Description |
|---|---|---|
heading | string | Sets the section's visible heading. |
id | string | A unique identifier for the section. |
Anchor to Removed propertiesRemoved properties
Anchor to onPress and toon Press and to
The previous ResourceItem onPress and to props made the whole item interactive or turned it into a link. The Polaris section is purely structural and doesn't handle interaction directly. Wrap the content you want to make interactive in <s-clickable>: set href to replace to, or read presses from its click event to replace onPress.
Buttons in the primary-action and secondary-actions slots stay interactive on their own and don't need to be wrapped.
Migrating onPress and to to s-clickable
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-section heading="Order #1001">
<s-clickable href="shopify:customer-account/orders/1001">
<s-text>Placed on April 12, 2026</s-text>
</s-clickable>
<s-button slot="primary-action" variant="primary">
Buy again
</s-button>
</s-section>
);
}Pre-Polaris (2025-07)
import {
Button,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem
to="shopify:customer-account/orders/1001"
action={<Button kind="primary">Buy again</Button>}
>
<Text>Order #1001</Text>
<Text>Shipped</Text>
</ResourceItem>
);
}The previous actionLabel and actionAccessibilityLabel props have been removed. Buttons placed in the primary-action and secondary-actions slots carry their own label and accessibilityLabel.
By default, the secondary-actions slot renders extra buttons inline when there's room and collapses the rest into a menu at narrower widths, using its own built-in trigger. To set the label on that trigger (the equivalent of the previous actionLabel), replace the slot's built-in trigger with your own button that targets an <s-menu> with commandFor. Opting in to this pattern turns off the automatic responsive behavior: the actions stay behind the menu at every width instead of rendering inline when there's space.
Customizing the menu trigger label
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-section heading="Order #1001">
<s-text>Placed on April 12, 2026</s-text>
<s-button slot="primary-action" variant="primary">
Buy again
</s-button>
<s-button
slot="secondary-actions"
variant="secondary"
commandFor="order-actions-menu"
>
Manage
</s-button>
<s-menu id="order-actions-menu">
<s-button>Track shipment</s-button>
<s-button>Return</s-button>
</s-menu>
</s-section>
);
}Pre-Polaris (2025-07)
import {
Button,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem
actionLabel="Manage"
action={
<>
<Button kind="primary">Buy again</Button>
<Button kind="secondary">Track shipment</Button>
<Button kind="secondary">Return</Button>
</>
}
>
<Text>Order #1001</Text>
<Text>Placed on April 12, 2026</Text>
</ResourceItem>
);
}Anchor to loadingloading
The previous loading prop rendered a loading state on the whole item. The Polaris section doesn't have a loading state. If you need to indicate loading, set loading on the button inside primary-action, or render a <s-spinner> alongside the content.
Anchor to Updated patternsUpdated patterns
Anchor to Matching the previous spacingMatching the previous spacing
The default gap between the heading, content, and action buttons is tighter in <s-section> than it was in ResourceItem. To approximate the previous spacing, wrap the section's content in <s-stack> with direction="block" and paddingBlock="large". Buttons stay in their slots and aren't wrapped.
Approximating the previous spacing
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-section heading="Order #1001">
<s-stack direction="block" paddingBlock="large">
<s-text>Placed on April 12, 2026</s-text>
</s-stack>
<s-button slot="primary-action" variant="primary">
Buy again
</s-button>
<s-button slot="secondary-actions">View details</s-button>
</s-section>
);
}Pre-Polaris (2025-07)
import {
Button,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem
action={
<>
<Button kind="primary">Buy again</Button>
<Button kind="secondary">View details</Button>
</>
}
>
<Text>Order #1001</Text>
<Text>Placed on April 12, 2026</Text>
</ResourceItem>
);
}Anchor to Using s-image-group inside s-sectionUsing s-image-group inside s-section
When <s-image-group> is rendered inside an <s-section>, it always uses a grid layout. The variant prop has been removed — the grid layout is implicit inside a section, so drop variant from existing usages.
Two other props have been removed:
accessibilityLabel— describe each image with its ownaltattribute instead.loading— each<s-image>manages its own loading state automatically.
Migrating image-group inside a section
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-section heading="Wishlist">
<s-image-group totalItems={7}>
<s-image src="https://example.com/product-1.jpg" alt="Blue t-shirt" />
<s-image src="https://example.com/product-2.jpg" alt="Red mug" />
<s-image src="https://example.com/product-3.jpg" alt="Canvas tote" />
<s-image src="https://example.com/product-4.jpg" alt="Notebook" />
</s-image-group>
</s-section>
);
}Pre-Polaris (2025-07)
import {
Image,
ImageGroup,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem>
<Text>Wishlist</Text>
<ImageGroup variant="grid" totalItems={7}>
<Image source="https://example.com/product-1.jpg" accessibilityDescription="Blue t-shirt" />
<Image source="https://example.com/product-2.jpg" accessibilityDescription="Red mug" />
<Image source="https://example.com/product-3.jpg" accessibilityDescription="Canvas tote" />
<Image source="https://example.com/product-4.jpg" accessibilityDescription="Notebook" />
</ImageGroup>
</ResourceItem>
);
}Anchor to Recreating the inline-stack image group layoutRecreating the inline-stack image group layout
The grid layout is the preferred way to display multiple images inside a section. If you need the overlapping inline-stack look from the previous ImageGroup, you can recreate it with an <s-grid> that uses overlapping column widths:
Recreating inline-stack with s-grid
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-section heading="Wishlist">
<s-grid gridTemplateColumns="28px 28px 28px 42px" alignItems="center">
<s-box
inlineSize="42px"
blockSize="42px"
border="base"
borderRadius="base"
overflow="hidden"
>
<s-image
src="https://example.com/product-1.jpg"
alt="Blue t-shirt"
aspectRatio="1/1"
objectFit="cover"
/>
</s-box>
<s-box
inlineSize="42px"
blockSize="42px"
border="base"
borderRadius="base"
overflow="hidden"
>
<s-image
src="https://example.com/product-2.jpg"
alt="Red mug"
aspectRatio="1/1"
objectFit="cover"
/>
</s-box>
<s-box
inlineSize="42px"
blockSize="42px"
border="base"
borderRadius="base"
overflow="hidden"
>
<s-image
src="https://example.com/product-3.jpg"
alt="Canvas tote"
aspectRatio="1/1"
objectFit="cover"
/>
</s-box>
<s-stack
direction="inline"
inlineSize="42px"
blockSize="42px"
borderRadius="large-100"
border="large base solid"
background="subdued"
alignItems="center"
justifyContent="center"
>
<s-text>+3</s-text>
</s-stack>
</s-grid>
</s-section>
);
}Pre-Polaris (2025-07)
import {
Image,
ImageGroup,
ResourceItem,
Text,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.profile.block.render',
() => <Extension />,
);
function Extension() {
return (
<ResourceItem>
<Text>Wishlist</Text>
<ImageGroup variant="inline-stack" totalItems={6}>
<Image source="https://example.com/product-1.jpg" accessibilityDescription="Blue t-shirt" />
<Image source="https://example.com/product-2.jpg" accessibilityDescription="Red mug" />
<Image source="https://example.com/product-3.jpg" accessibilityDescription="Canvas tote" />
</ImageGroup>
</ResourceItem>
);
}