Version 2025-07 is the last API version to support React-based UI components. Later versions use web components, native UI elements with built-in accessibility, better performance, and consistent styling with Shopify's design system. Check out the migration guide to upgrade your extension.
Modal
The Modal component displays content in an overlay that shifts focus towards a specific action or set of information before the main flow can proceed.
The library automatically applies the WAI-ARIA Dialog pattern to both the activator and the modal content. For contextual content that doesn't require full focus, use Popover instead.
Supported targets
- Customer
Account::Kitchen Sink - customer-account.
footer. render-after - customer-account.
order-index. announcement. render - customer-account.
order-index. block. render - customer-account.
order-status. announcement. render - customer-account.
order-status. block. render - customer-account.
order-status. cart-line-item. render-after - customer-account.
order-status. cart-line-list. render-after - customer-account.
order-status. customer-information. render-after - customer-account.
order-status. fulfillment-details. render-after - customer-account.
order-status. payment-details. render-after - customer-account.
order-status. return-details. render-after - customer-account.
order-status. unfulfilled-items. render-after - customer-account.
order. action. menu-item. render - customer-account.
order. action. render - customer-account.
order. page. render - customer-account.
page. render - customer-account.
profile. addresses. render-after - customer-account.
profile. announcement. render - customer-account.
profile. block. render - customer-account.
profile. company-details. render-after - customer-account.
profile. company-location-addresses. render-after - customer-account.
profile. company-location-payment. render-after - customer-account.
profile. company-location-staff. render-after - customer-account.
profile. payment. render-after
Supported targets
- Customer
Account::Kitchen Sink - customer-account.
footer. render-after - customer-account.
order-index. announcement. render - customer-account.
order-index. block. render - customer-account.
order-status. announcement. render - customer-account.
order-status. block. render - customer-account.
order-status. cart-line-item. render-after - customer-account.
order-status. cart-line-list. render-after - customer-account.
order-status. customer-information. render-after - customer-account.
order-status. fulfillment-details. render-after - customer-account.
order-status. payment-details. render-after - customer-account.
order-status. return-details. render-after - customer-account.
order-status. unfulfilled-items. render-after - customer-account.
order. action. menu-item. render - customer-account.
order. action. render - customer-account.
order. page. render - customer-account.
page. render - customer-account.
profile. addresses. render-after - customer-account.
profile. announcement. render - customer-account.
profile. block. render - customer-account.
profile. company-details. render-after - customer-account.
profile. company-location-addresses. render-after - customer-account.
profile. company-location-payment. render-after - customer-account.
profile. company-location-staff. render-after - customer-account.
profile. payment. render-after
Anchor to PropertiesProperties
Configure the following properties on the Modal component.
- Anchor to accessibilityLabelaccessibilityLabelaccessibilityLabelstringstring
A label that describes the purpose of the modal, announced by screen readers. If not set, it will use the value of
title.- Anchor to idididstringstring
A unique identifier for the modal. When no
idis set, a globally unique value will be used instead.- Anchor to onCloseonCloseonClose() => void() => void
A callback fired when the modal is closed. This is triggered when the close button, the backdrop, or the
escapekey are pressed.- Anchor to onOpenonOpenonOpen() => void() => void
A callback fired when the modal is opened. This is called at the beginning of the transition that opens the modal.
- Anchor to paddingpaddingpaddingbooleanboolean
Whether to add default spacing around both the header (which holds the
title) and the content of the modal.- Anchor to primaryActionprimaryActionprimaryActionRemoteFragmentRemoteFragment
The primary action to perform, provided as a button component. Only one button can be rendered.
- Anchor to secondaryActionssecondaryActionssecondaryActionsRemoteFragmentRemoteFragment
The secondary actions to perform, provided as button components. Only one button can be rendered.
- Anchor to sizesizesize'small' | 'auto' | 'large' | 'max''small' | 'auto' | 'large' | 'max'Default: 'auto'Default: 'auto'
Adjust the size of the modal.
'small': A compact modal for simple confirmations or short messages.'auto': Automatically sizes the modal based on its content.'large': A large modal for complex content or forms.'max': Expands the modal to its maximum size, on both the horizontal and vertical axes.
- Anchor to titletitletitlestringstring
A title rendered at the top of the modal.
Anchor to ExamplesExamples
Anchor to Display a return policyDisplay a return policy
Present information that requires customer acknowledgment. This example shows a modal triggered by a link, with body text and a close button that calls ui.overlay.close().
Display a return policy

Display a return policy
React
import {
reactExtension,
useApi,
Button,
Link,
Modal,
TextBlock,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.page.render',
() => <Extension />,
);
function Extension() {
const {ui} = useApi();
return (
<Link
overlay={
<Modal
id="my-modal"
padding
title="Return policy"
>
<TextBlock>
We have a 30-day return policy, which
means you have 30 days after receiving
your item to request a return.
</TextBlock>
<TextBlock>
To be eligible for a return, your item
must be in the same condition that you
received it, unworn or unused, with
tags, and in its original packaging.
You’ll also need the receipt or proof
of purchase.
</TextBlock>
<Button
onPress={() =>
ui.overlay.close('my-modal')
}
>
Close
</Button>
</Modal>
}
>
Return policy
</Link>
);
}JS
import {
extension,
Button,
Link,
Modal,
TextBlock,
} from '@shopify/ui-extensions/customer-account';
export default extension('customer-account.page.render', (root, {ui}) => {
const modalFragment = root.createFragment();
const modal = root.createComponent(
Modal,
{id: 'my-modal', title: 'Return policy', padding: true},
[
root.createComponent(
TextBlock,
undefined,
'We have a 30-day return policy, which means you have 30 days after receiving your item to request a return.',
),
root.createComponent(
TextBlock,
undefined,
'To be eligible for a return, your item must be in the same condition that you received it, unworn or unused, with tags, and in its original packaging. You’ll also need the receipt or proof of purchase.',
),
root.createComponent(
Button,
{
onPress() {
ui.overlay.close('my-modal');
},
},
'Close',
),
],
);
modalFragment.appendChild(modal);
const link = root.createComponent(
Link,
{overlay: modalFragment},
'Return policy',
);
root.appendChild(link);
});Anchor to Confirm a destructive actionConfirm a destructive action
Pair a cancel button with a destructive action button to prevent accidental operations. This example presents a subscription cancellation confirmation with keep and cancel options.
Confirm a destructive action
React
import {
reactExtension,
useApi,
Button,
Modal,
TextBlock,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.order-status.block.render',
() => <Extension />,
);
function Extension() {
const {ui} = useApi();
return (
<Button
overlay={
<Modal
id="cancel-modal"
padding
title="Cancel subscription"
>
<TextBlock>
Are you sure you want to cancel your
subscription? You'll lose access to all
premium features at the end of your
current billing period.
</TextBlock>
<Button
onPress={() =>
ui.overlay.close('cancel-modal')
}
>
Keep subscription
</Button>
<Button
onPress={() => {
// Handle cancellation
ui.overlay.close('cancel-modal');
}}
>
Cancel subscription
</Button>
</Modal>
}
>
Cancel subscription
</Button>
);
}JS
import {
extension,
Button,
Modal,
TextBlock,
} from '@shopify/ui-extensions/customer-account';
export default extension(
'customer-account.order-status.block.render',
(root, {ui}) => {
const modalFragment = root.createFragment();
const modal = root.createComponent(
Modal,
{id: 'cancel-modal', title: 'Cancel subscription', padding: true},
[
root.createComponent(
TextBlock,
undefined,
'Are you sure you want to cancel your subscription? You\u2019ll lose access to all premium features at the end of your current billing period.',
),
root.createComponent(
Button,
{
onPress() {
ui.overlay.close('cancel-modal');
},
},
'Keep subscription',
),
root.createComponent(
Button,
{
onPress() {
// Handle cancellation
ui.overlay.close('cancel-modal');
},
},
'Cancel subscription',
),
],
);
modalFragment.appendChild(modal);
const button = root.createComponent(
Button,
{overlay: modalFragment},
'Cancel subscription',
);
root.appendChild(button);
},
);Anchor to Collect input with a formCollect input with a form
Gather information without leaving the current context. This example embeds a feedback form with a select field and text area inside the modal.
Collect input with a form
React
import {
reactExtension,
useApi,
Button,
Modal,
Select,
TextField,
} from '@shopify/ui-extensions-react/customer-account';
export default reactExtension(
'customer-account.order-status.block.render',
() => <Extension />,
);
function Extension() {
const {ui} = useApi();
return (
<Button
overlay={
<Modal
id="feedback-modal"
padding
title="Share your feedback"
>
<Select
label="Topic"
options={[
{value: 'delivery', label: 'Delivery'},
{value: 'product', label: 'Product quality'},
{value: 'support', label: 'Customer support'},
]}
/>
<TextField
label="Comments"
multiline={3}
/>
<Button
onPress={() =>
ui.overlay.close('feedback-modal')
}
>
Submit feedback
</Button>
</Modal>
}
>
Leave feedback
</Button>
);
}JS
import {
extension,
Button,
Modal,
Select,
TextField,
} from '@shopify/ui-extensions/customer-account';
export default extension(
'customer-account.order-status.block.render',
(root, {ui}) => {
const modalFragment = root.createFragment();
const modal = root.createComponent(
Modal,
{id: 'feedback-modal', title: 'Share your feedback', padding: true},
[
root.createComponent(Select, {
label: 'Topic',
options: [
{value: 'delivery', label: 'Delivery'},
{value: 'product', label: 'Product quality'},
{value: 'support', label: 'Customer support'},
],
}),
root.createComponent(TextField, {
label: 'Comments',
multiline: 3,
}),
root.createComponent(
Button,
{
onPress() {
ui.overlay.close('feedback-modal');
},
},
'Submit feedback',
),
],
);
modalFragment.appendChild(modal);
const button = root.createComponent(
Button,
{overlay: modalFragment},
'Leave feedback',
);
root.appendChild(button);
},
);Anchor to Best practicesBest practices
- Use for focused, specific tasks: Modals work best when customers need to make a decision or acknowledge critical information. Don't use them for contextual tools or actions that could happen on the page directly.
- Include a prominent call to action: Every modal should have a clear primary action so customers know what to do next.
- Don't nest modals: Avoid launching one modal from another. If the workflow requires multiple steps, reconsider the design.
- Use specific action verbs: Label buttons with clear verbs like "Cancel subscription", "Save", or "Continue" rather than vague terms like "Yes", "OK", or "Submit".
- Explain destructive consequences: For destructive actions, describe what'll happen in the modal body before the customer confirms.
Anchor to LimitationsLimitations
- Modals can only be opened by user interaction through an activator component, not programmatically on page load.
- The modal always renders centered in the viewport and can't be repositioned.