Migrate to the Polaris modal component
The Polaris modal component displays content in an overlay dialog that requires customer interaction. It replaces the previous Modal component and is available as <s-modal> in API versions 2025-10 and newer.
Anchor to Updated propertiesUpdated properties
The following properties are different in the Polaris modal component.
Anchor to titletitle
The previous Modal title prop is now called heading.
Anchor to paddingpadding
The padding prop changed from a boolean to an enum.
| Previous value | New value | Migration notes |
|---|---|---|
true | 'base' | 'base' is the default. |
false | 'none' | Use padding="none" to remove padding. |
Migrating padding values
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<>
<s-button command="--show" commandFor="settings-modal" variant="primary">
Open settings
</s-button>
<s-modal id="settings-modal" heading="Settings" padding="none">
<s-paragraph>Content of the modal</s-paragraph>
</s-modal>
</>
);
}Pre-Polaris (2025-07)
import {
reactExtension,
Button,
Modal,
TextBlock,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);
function Extension() {
return (
<Button
overlay={
<Modal id="settings-modal" title="Settings" padding={false}>
<TextBlock>Content of the modal</TextBlock>
</Modal>
}
>
Open settings
</Button>
);
}Anchor to sizesize
The size prop values have changed.
| Previous value | New value | Migration notes |
|---|---|---|
'small' | 'small' or 'small-100' | 'small' is an alias for 'small-100'. |
'auto' | 'base' | 'base' is the default. |
'large' | 'large' or 'large-100' | 'large' is an alias for 'large-100'. |
'max' | 'max' | No change needed. |
For more on the scale system, see Scale.
The previous Modal primaryAction and secondaryActions props have been replaced with the primary-action and secondary-actions slots.
| Previous prop | New pattern | Migration notes |
|---|---|---|
primaryAction | slot="primary-action" | Place button children with slot="primary-action". |
secondaryActions | slot="secondary-actions" | Place button children with slot="secondary-actions". |
Migrating action props 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-button command="--show" commandFor="confirm-modal">
Delete item
</s-button>
<s-modal id="confirm-modal" heading="Confirm deletion">
<s-paragraph>Are you sure you want to delete this item?</s-paragraph>
<s-button slot="primary-action" command="--hide" commandFor="confirm-modal">
Confirm
</s-button>
<s-button slot="secondary-actions" command="--hide" commandFor="confirm-modal">
Cancel
</s-button>
</s-modal>
</>
);
}Pre-Polaris (2025-07)
import {
reactExtension,
Button,
Modal,
TextBlock,
useApi,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);
function Extension() {
const {ui} = useApi();
return (
<Button
overlay={
<Modal
id="confirm-modal"
title="Confirm deletion"
primaryAction={
<Button onPress={() => ui.overlay.close('confirm-modal')}>
Confirm
</Button>
}
secondaryActions={
<Button onPress={() => ui.overlay.close('confirm-modal')}>
Cancel
</Button>
}
>
<TextBlock>Are you sure you want to delete this item?</TextBlock>
</Modal>
}
>
Delete item
</Button>
);
}The previous onClose prop is now called onHide. The previous onOpen prop is now called onShow.
| Previous prop | New prop |
|---|---|
onClose | onHide |
onOpen | onShow |
Migrating onClose and onOpen
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
return (
<>
<s-button command="--show" commandFor="welcome-modal">
Open welcome
</s-button>
<s-modal
id="welcome-modal"
heading="Welcome"
onShow={() => console.log('Modal opened')}
onHide={() => console.log('Modal closed')}
>
<s-paragraph>Welcome to our store!</s-paragraph>
</s-modal>
</>
);
}Pre-Polaris (2025-07)
import {
reactExtension,
Modal,
TextBlock,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);
function Extension() {
return (
<Modal
id="welcome-modal"
title="Welcome"
onOpen={() => console.log('Modal opened')}
onClose={() => console.log('Modal closed')}
>
<TextBlock>Welcome to our store!</TextBlock>
</Modal>
);
}Anchor to Removed propertiesRemoved properties
Anchor to overlayoverlay
The previous pattern of using the overlay prop on Pressable or Button to render a modal inline is no longer supported. Instead, render the modal as a sibling element and use command with commandFor to open it.
The command prop defaults to --auto, which resolves to --toggle for modals. You can omit command when targeting a modal and only set commandFor.
The command prop defaults to --auto, which resolves to --toggle for modals. You can omit command when targeting a modal and only set commandFor.
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-button command="--show" commandFor="details-modal">
More info
</s-button>
<s-modal id="details-modal" heading="Details">
<s-paragraph>Delivery in 2 to 4 business days.</s-paragraph>
</s-modal>
</>
);
}Pre-Polaris (2025-07)
import {
reactExtension,
Button,
Modal,
TextBlock,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);
function Extension() {
return (
<Button
overlay={
<Modal id="details-modal" title="Details">
<TextBlock>Delivery in 2 to 4 business days.</TextBlock>
</Modal>
}
>
More info
</Button>
);
}Anchor to New propertiesNew properties
The Polaris modal component introduces the following new properties:
| New prop | Type | Description |
|---|---|---|
onAfterShow | event | Fires after the modal has fully opened and animations have completed. |
onAfterHide | event | Fires after the modal has fully closed and animations have completed. |
size values 'small-100', 'large-100' | — | New size options on the scale. 'small' and 'large' remain available as aliases. |
Anchor to New methodsNew methods
The Polaris modal component introduces a hideOverlay() method for programmatically closing the modal. This replaces the previous pattern of using useApi() and ui.overlay.close() to close overlays.
Using hideOverlay to close the modal
Latest (Preact)
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
import {useRef} from 'preact/hooks';
export default function extension() {
render(<Extension />, document.body);
}
function Extension() {
const modalRef = useRef(null);
function handleConfirm() {
// Process action, then close the modal
modalRef.current?.hideOverlay();
}
return (
<>
<s-button command="--show" commandFor="action-modal">
Take action
</s-button>
<s-modal id="action-modal" heading="Confirm action" ref={modalRef}>
<s-paragraph>Are you sure?</s-paragraph>
<s-button slot="primary-action" onClick={handleConfirm}>
Confirm
</s-button>
</s-modal>
</>
);
}Pre-Polaris (2025-07)
import {
reactExtension,
Button,
Modal,
TextBlock,
useApi,
} from '@shopify/ui-extensions-react/checkout';
export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);
function Extension() {
const {ui} = useApi();
function handleConfirm() {
// Process action, then close the modal
ui.overlay.close('action-modal');
}
return (
<Modal
id="action-modal"
title="Confirm action"
primaryAction={
<Button onPress={handleConfirm}>Confirm</Button>
}
>
<TextBlock>Are you sure?</TextBlock>
</Modal>
);
}