Migrate Page to the Polaris page component
The Polaris page component is the outer wrapper of a full page, including the page heading, subheading, and page-level actions. It replaces the previous Page component and is available as <s-page> in API versions 2025-10 and newer.
Migrating Page to s-page
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-page heading="Order #1411" subheading="Confirmed Oct 5">
<s-button slot="primary-action" onClick={() => {}}>
Buy again
</s-button>
<s-text>Content</s-text>
</s-page>
);
}Pre-Polaris (2025-07)
import {
Button,
Page,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.page.render',
() => <Extension />,
);
function Extension() {
return (
<Page
title="Order #1411"
subtitle="Confirmed Oct 5"
primaryAction={<Button onPress={() => {}}>Buy again</Button>}
>
Content
</Page>
);
}Anchor to Updated propertiesUpdated properties
Anchor to titletitle
The previous Page title prop has been renamed to heading. It's now optional.
Anchor to subtitlesubtitle
The previous Page subtitle prop has been renamed to subheading.
Anchor to primaryActionprimary Action
The previous Page primaryAction prop accepted a RemoteFragment containing one or more Button components, optionally grouped behind an overflow label. The Polaris page component splits this into two slots:
- Place the first action in the
primary-actionslot. This slot accepts a single button and renders it as the prominent primary action. - Place any additional actions in the
secondary-actionsslot.
| Previous prop | New slot |
|---|---|
primaryAction={<Button>…</Button>} (single button) | <s-button slot="primary-action">…</s-button> |
primaryAction={<><Button>A</Button><Button>B</Button></>} (multiple buttons) | First button in the primary-action slot, remaining buttons in the secondary-actions slot |
Buttons in the primary-action and secondary-actions slots share the same supported properties. See Primary and secondary slot button properties.
Migrating multiple primary actions 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-page heading="Order #1411">
<s-button slot="primary-action" onClick={() => {}}>
Buy again
</s-button>
<s-button slot="secondary-actions" onClick={() => {}}>
Track package
</s-button>
<s-button slot="secondary-actions" onClick={() => {}}>
Return items
</s-button>
<s-text>Order details</s-text>
</s-page>
);
}Pre-Polaris (2025-07)
import {
Button,
Page,
TextBlock,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.page.render',
() => <Extension />,
);
function Extension() {
return (
<Page
title="Order #1411"
primaryAction={
<>
<Button onPress={() => {}}>Buy again</Button>
<Button onPress={() => {}}>Track package</Button>
<Button onPress={() => {}}>Return items</Button>
</>
}
>
<TextBlock>Order details</TextBlock>
</Page>
);
}Anchor to secondaryActionsecondary Action
The previous Page secondaryAction prop rendered as a back-arrow breadcrumb link in the page header. It's been renamed to the breadcrumb-actions slot. Place an <s-button> in the slot to replace the previous prop.
| Previous prop | New slot |
|---|---|
secondaryAction={<Button>Label</Button>} | <s-button slot="breadcrumb-actions" accessibilityLabel="Label" /> |
Buttons in the breadcrumb-actions slot only support a restricted set of properties, and any children are discarded. Use accessibilityLabel to describe the breadcrumb's destination. See Breadcrumb slot button properties for the full restricted set.
Migrating secondaryAction to breadcrumb-actions
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-page heading="Order #1411">
<s-button
slot="breadcrumb-actions"
accessibilityLabel="Back to orders"
href="shopify:customer-account/orders"
/>
<s-text>Order details</s-text>
</s-page>
);
}Pre-Polaris (2025-07)
import {
Button,
Page,
TextBlock,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.page.render',
() => <Extension />,
);
function Extension() {
return (
<Page
title="Order #1411"
secondaryAction={
<Button to="shopify:customer-account/orders">
Orders
</Button>
}
>
<TextBlock>Order details</TextBlock>
</Page>
);
}Anchor to Removed propertiesRemoved properties
Anchor to primaryActionLabel and primaryActionAccessibilityLabelprimary Action Label and primary Action Accessibility Label
The previous primaryActionLabel and primaryActionAccessibilityLabel props set a custom label on the overflow trigger that grouped multiple primary action buttons together. With the new slot split (see primaryAction), actions render directly into the primary-action and secondary-actions slots and the overflow trigger is managed automatically.
When the secondary-actions slot contains multiple buttons, the extras collapse into a menu with a built-in trigger. The <s-page> web component doesn't expose a prop to customize that trigger's label. If you need control over the label (the equivalent of the previous primaryActionLabel), replace the slot's built-in trigger with your own button that targets an <s-menu> with commandFor.
Customizing the secondary actions 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-page heading="Order #1411">
<s-button slot="primary-action" onClick={() => {}}>
Buy again
</s-button>
<s-button
slot="secondary-actions"
commandFor="order-actions-menu"
>
Manage
</s-button>
<s-menu id="order-actions-menu">
<s-button>Track package</s-button>
<s-button>Return items</s-button>
</s-menu>
<s-text>Order details</s-text>
</s-page>
);
}Pre-Polaris (2025-07)
import {
Button,
Page,
TextBlock,
reactExtension,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.page.render',
() => <Extension />,
);
function Extension() {
return (
<Page
title="Order #1411"
primaryActionLabel="Manage"
primaryAction={
<>
<Button onPress={() => {}}>Buy again</Button>
<Button onPress={() => {}}>Track package</Button>
<Button onPress={() => {}}>Return items</Button>
</>
}
>
<TextBlock>Order details</TextBlock>
</Page>
);
}Anchor to loadingloading
The previous loading prop rendered a loading state on the page-owned UI elements, such as the action buttons in the header. The Polaris page doesn't have a page-level loading state. If you need to indicate loading, set loading on the button inside primary-action, or render an <s-spinner> alongside the content.
Buttons placed in the primary-action and secondary-actions slots only support a subset of the <s-button> properties.
The following props from the previous Button component are no longer supported inside action slots:
| Removed prop | Migration notes |
|---|---|
loadingLabel | Removed. Use the default loading indicator by setting loading. |
overlay | Removed. Use command and commandFor to target a sibling component by ID. |
To open a modal when the slot button is activated, target a sibling <s-modal> by ID. To close the modal, use command="--hide" with commandFor, or call the modal's hideOverlay() method.
Migrating overlay to command and commandFor
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<s-page heading="Order #1411">
<s-button
slot="primary-action"
command="--show"
commandFor="cancel-order"
>
Cancel order
</s-button>
<s-modal id="cancel-order" heading="Cancel order">
<s-text>Are you sure? This action can't be undone.</s-text>
<s-button
slot="primary-action"
command="--hide"
commandFor="cancel-order"
>
Confirm cancellation
</s-button>
</s-modal>
<s-text>Order details</s-text>
</s-page>
);
}Pre-Polaris (2025-07)
import {
Button,
Modal,
Page,
TextBlock,
reactExtension,
useApi,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.page.render',
() => <Extension />,
);
function Extension() {
const {ui} = useApi();
return (
<Page
title="Order #1411"
primaryAction={
<Button
overlay={
<Modal
id="cancel-order"
title="Cancel order"
primaryAction={
<Button onPress={() => ui.overlay.close('cancel-order')}>
Confirm cancellation
</Button>
}
>
<TextBlock>
Are you sure? This action can't be undone.
</TextBlock>
</Modal>
}
>
Cancel order
</Button>
}
>
<TextBlock>Order details</TextBlock>
</Page>
);
}Anchor to onPresson Press
The previous Button onPress prop is now called onClick.
The previous Button to prop is now called href.
Slot buttons support the following new properties:
| New prop | Description |
|---|---|
command | Sets the action to run on the target component when the button is activated. |
commandFor | Sets the ID of the target component for the command. |
Buttons placed in the breadcrumb-actions slot render as a back-arrow breadcrumb link in the page header and only support a restricted set of <s-button> properties. Children aren't supported — describe the destination with accessibilityLabel instead.
Anchor to Supported propertiesSupported properties
| Prop | Description |
|---|---|
accessibilityLabel | Required. A label that describes the breadcrumb's destination to assistive technologies. The button's text content is discarded, so this label is what screen readers announce. |
onClick | A callback that fires when the breadcrumb is activated. Replaces the previous Button onPress prop. |
href | The URL to navigate to when the breadcrumb is activated. Replaces the previous Button to prop. |
All other Button props from the previous API are unsupported in the breadcrumb-actions slot.