Modal Component
@shopify/app-bridge-react@v4
and the app-bridge.js
script tagThe Modal API allows you to display an overlay that prevents interaction with the rest of the app until dismissed.
It is used by customizing your Modal content with the Modal
component and then opening it with the shopify.modal.show('modal-id')
API.
Anchor to modal componentModal component
The Modal
component is available for use in your app. It configures a Modal to display in the Shopify Admin.
The content you provide can be simple React elements or a src
prop with a URL that will be loaded.
- Anchor to childrenchildrenHTMLCollection &
The content to display within a Modal. You can provide a single HTML element with children and the ui-title-bar element to configure the Modal title bar.
- string
A unique identifier for the Modal
- string
The URL of the content to display within a Modal. If provided, the Modal will display the content from the provided URL and any children other than the ui-title-bar and ui-save-bar elements will be ignored.
- Anchor to variantvariant'small' | 'base' | 'large' | 'max'Default: "base"
The size of the modal.
Before the Modal is shown, this can be changed to any of the provided values. After the Modal is shown, this can can only be changed between
small
,base
, andlarge
.
_UIModalAttributes
- children
The content to display within a Modal. You can provide a single HTML element with children and the [ui-title-bar](/docs/api/app-bridge-library/web-components/ui-title-bar) element to configure the Modal title bar.
HTMLCollection & UITitleBarAttributes
- id
A unique identifier for the Modal
string
- src
The URL of the content to display within a Modal. If provided, the Modal will display the content from the provided URL and any children other than the [ui-title-bar](/docs/api/app-bridge-library/web-components/ui-title-bar) and [ui-save-bar](/docs/api/app-bridge-library/web-components/ui-save-bar) elements will be ignored.
string
- variant
The size of the modal. Before the Modal is shown, this can be changed to any of the provided values. After the Modal is shown, this can can only be changed between `small`, `base`, and `large`.
'small' | 'base' | 'large' | 'max'
export interface _UIModalAttributes {
/**
* A unique identifier for the Modal
*/
id?: string;
/**
* The size of the modal.
*
* Before the Modal is shown, this can be changed to any of the provided values.
* After the Modal is shown, this can can only be changed between `small`, `base`, and `large`.
*
* @defaultValue "base"
*/
variant?: 'small' | 'base' | 'large' | 'max';
/**
* The URL of the content to display within a Modal.
* If provided, the Modal will display the content from the provided URL
* and any children other than the [ui-title-bar](/docs/api/app-bridge-library/web-components/ui-title-bar)
* and [ui-save-bar](/docs/api/app-bridge-library/web-components/ui-save-bar) elements will be ignored.
*/
src?: string;
/**
* The content to display within a Modal.
* You can provide a single HTML element with children
* and the [ui-title-bar](/docs/api/app-bridge-library/web-components/ui-title-bar)
* element to configure the Modal title bar.
*/
children?: HTMLCollection & UITitleBarAttributes;
}
UITitleBarAttributes
- children
The children of the title bar.
any
- title
The title of the title bar. Can also be set via <code>document.title</code>.
string
export interface UITitleBarAttributes extends _UITitleBarAttributes {
children?: any;
}
Modal
examples
Modal
import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal"> <p>Message</p> <TitleBar title="Title"> <button variant="primary">Label</button> <button onClick={() => shopify.modal.hide('my-modal')}>Label</button> </TitleBar> </Modal> </> ); }
Preview

Anchor to examplesExamples
Modal options, variations, and events
Anchor to example-modals-with-different-optionsModals with different options
Anchor to example-opening-a-max-size-modalOpening a max size Modal
Modal with max size
Anchor to example-specifying-a-modal-sizeSpecifying a Modal size
Modal with variant
Anchor to example-specifying-a-title-for-the-modalSpecifying a title for the Modal
Modal with title
Anchor to example-adding-primary-and-secondary-actions-to-a-modalAdding primary and secondary actions to a Modal
Modal with primary and secondary actions
Anchor to example-using-a-modal-for-a-destructive-actionUsing a modal for a destructive action
Adding a critical button to a Modal
Opening a max size Modal
examples
Opening a max size Modal
description
Modal with max size
import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal" variant="max"> <div></div> <TitleBar> <button variant="primary">Primary action</button> <button>Secondary action</button> </TitleBar> </Modal> </> ); }
Specifying a Modal size
description
Modal with variant
import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal" variant="large"> <div></div> <TitleBar> <button variant="primary">Primary action</button> <button>Secondary action</button> </TitleBar> </Modal> </> ); }
Specifying a title for the Modal
description
Modal with title
import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal"> <p>Hello, World!</p> <TitleBar title="My Modal"></TitleBar> </Modal> </> ); }
Adding primary and secondary actions to a Modal
description
Modal with primary and secondary actions
import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal"> <p>Hello, World!</p> <TitleBar> <button variant="primary" onClick={() => console.log('Saving')}> Save </button> <button onClick={() => console.log('Cancelling')}>Cancel</button> </TitleBar> </Modal> </> ); }
Using a modal for a destructive action
description
Adding a critical button to a Modal
import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal"> <p>If you delete this resource, it can't be undone.</p> <TitleBar title="Delete this resource"> <button variant="primary" tone="critical" onClick={() => console.log('Deleting')} > Delete </button> <button onClick={() => console.log('Cancelling')}>Cancel</button> </TitleBar> </Modal> </> ); }
Preview

Anchor to example-modal-eventsModal events
Anchor to example-subscribing-to-showSubscribing to Show
Using the callback which is called when the Modal is opened
Anchor to example-subscribing-to-hideSubscribing to Hide
Using the callback which is called when the Modal is closed
Subscribing to Show
examples
Subscribing to Show
description
Using the `onShow` callback which is called when the Modal is opened
import {Modal, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal" onShow={() => console.log('Modal is showing')}> <p>Message</p> </Modal> </> ); }
Subscribing to Hide
description
Using the `onHide` callback which is called when the Modal is closed
import {Modal, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal" onHide={() => console.log('Modal is hiding')}> <button onclick="document.getElementById('my-modal').hide()"> Hide Modal </button> </Modal> </> ); }
Anchor to example-modal-controlsModal controls
Anchor to example-showing-a-modal-with-the-`open`-propShowing a Modal with the `open` prop
Modal controlled by the open
prop
Showing a Modal with the `open` prop
examples
Showing a Modal with the `open` prop
description
Modal controlled by the `open` prop
import {useState} from 'react'; import {Modal, TitleBar} from '@shopify/app-bridge-react'; export function MyModal() { const [modalOpen, setModalOpen] = useState(false); return ( <> <button onClick={() => setModalOpen(true)}>Open Modal</button> <Modal id="my-modal" open={modalOpen}> <p>Message</p> <TitleBar title="My Modal"> <button onClick={() => setModalOpen(false)}>Label</button> </TitleBar> </Modal> </> ); }
Anchor to example-using-a-src-url-to-load-contentUsing a src URL to load content
Anchor to example-loading-content-from-a-urlLoading content from a URL
Loading content from a URL
Anchor to example-communicating-between-the-modal-and-the-parent-windowCommunicating between the Modal and the parent window
Communicating between the Modal and the parent window
Anchor to example-opening-a-base-modal-within-a-max-src-modalOpening a base modal within a max src modal
This pattern is useful for displaying a dialog or prompt from within a max variant modal.
Loading content from a URL
examples
Loading content from a URL
description
Loading content from a URL
// main app import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <button onClick={() => shopify.modal.show('my-modal')}>Open Modal</button> <Modal id="my-modal" src="/my-route"> <TitleBar title="Title"> <button variant="primary">Label</button> <button onClick={() => shopify.modal.hide('my-modal')}>Label</button> </TitleBar> </Modal> </> ); } // my-route.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="shopify-api-key" content="%SHOPIFY_API_KEY%" /> <script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script> </head> <body> <h1>My separate route</h1> <script src="./my-route.jsx" type="module"></script> </body> </html> // my-route.jsx export function MyRoute() { return ( <h1>My separate route</h1> ); }
Communicating between the Modal and the parent window
description
Communicating between the Modal and the parent window
// main app import {Modal, TitleBar, useAppBridge} from '@shopify/app-bridge-react'; import {useEffect} from 'react'; export function MyModal() { const shopify = useAppBridge(); useEffect(() => { function handleMessageFromModal(ev) { console.log('Message received in main app:', ev.data); } window.addEventListener('message', handleMessageFromModal) return () => { window.removeEventListener('message', handleMessageFromModal) } }, []) const openModal = async () => { await shopify.modal.show('my-modal'); sendMessageToModal('Hello from the main app'); } const sendMessageToModal = (message) => { document.getElementById('my-modal').contentWindow.postMessage(message, location.origin); } return ( <> <button onClick={openModal}>Open Modal</button> <Modal id="my-modal" src="/my-route"> <TitleBar title="Title"> <button variant="primary">Label</button> <button onClick={() => shopify.modal.hide('my-modal')}>Label</button> </TitleBar> </Modal> </> ); } // my-route.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="shopify-api-key" content="%SHOPIFY_API_KEY%" /> <script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script> </head> <body> <h1>My separate route</h1> <script src="./my-route.jsx" type="module"></script> </body> </html> // my-route.jsx import {useEffect} from 'react'; export function MyRoute() { useEffect(() => { function handleMessageFromMainApp(ev) { console.log('Message received in modal:', ev.data); } window.addEventListener('message', handleMessageFromMainApp) return () => { window.removeEventListener('message', handleMessageFromMainApp) } }, []) const sendMessageToMainApp = (message) => { window.opener.postMessage(message, location.origin); } return ( <button onClick={() => sendMessageToMainApp('Hello from the modal')}> Send message </button> ); }
Opening a base modal within a max src modal
description
This pattern is useful for displaying a dialog or prompt from within a max variant modal.
// main app import {Modal, useAppBridge} from '@shopify/app-bridge-react'; export function MyModal() { const shopify = useAppBridge(); return ( <> <Modal src="/my-route" variant="max" id="my-modal" /> <button onClick={() => shopify.modal.show("my-modal")}>Open</button> <Modal id="my-nested-modal"> <p>Message</p> </Modal> </> ); } // my-route.js <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="shopify-api-key" content="%SHOPIFY_API_KEY%" /> <script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script> </head> <body> <button onclick="window.opener.shopify.modal.show('my-nested-modal')">Open</button> </body> </html>