Skip to main content

Migrate your app to Shopify App Bridge React 4.x.x

If you have an app that uses components and hooks from Shopify App Bridge React 3.x.x, then you can follow this guide to upgrade your components and hooks to the latest version.



Anchor to Benefits of migrationBenefits of migration

When you migrate your app to use Shopify App Bridge React 4.x.x, you can take advantage of the following improvements to the developer experience:

Anchor to Simplified configurationSimplified configuration

Shopify App Bridge React 4.x.x simplifies the configuration process. Apps no longer need to use a React Provider or the host config. Instead, they only need to provide their API key to the app-bridge.js script.

The app-bridge.js script automatically keeps itself up to date, so you can access new Shopify App Bridge APIs as soon as they're released.

The latest version of Shopify App Bridge embraces the web platform and web standards, so you can use web APIs you're already familiar with. For more information about the motivation behind App Bridge, refer to Shopify's platform is the Web platform.

Take advantage of the new Direct API access feature. You can make requests to the Admin API directly from your app using the standard web fetch API. For more information about Direct API access, refer to the documentation.

Anchor to Modals with custom contentModals with custom content

Add modals to your app with custom content using the new Modal component from Shopify App Bridge React. This enables you to create rich modal experiences that render directly in the Shopify admin without having to handle separate routes. These modals are fast too, because they're preloaded.


Anchor to Step 1: Add the ,[object Object], script tagStep 1: Add the app-bridge.js script tag

Include the app-bridge.js script tag in your app. Replace %SHOPIFY_API_KEY% with your app's client ID. This configures your app to use Shopify App Bridge.

The app-bridge.js script is CDN-hosted, so your app always gets the latest version of it.

index.html

<head>
<meta name="shopify-api-key" content="%SHOPIFY_API_KEY%" />
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
</head>

Anchor to Step 2: Upgrade your ,[object Object], dependencyStep 2: Upgrade your @shopify/app-bridge-react dependency

Install or upgrade the @shopify/app-bridge-react dependency with your package manager.

Terminal

npm install @shopify/app-bridge-react@latest
yarn add @shopify/app-bridge-react@latest
pnpm add @shopify/app-bridge-react@latest

In previous versions of App Bridge React, you needed to set up a context provider to enable component and hook usage. This is no longer needed, as all the setup for Shopify App Bridge is done through the app-bridge.js script tag that you added in step 1.

Remove all Provider import statements and usages from @shopify/app-bridge-react in your app:

Provider setup

3.x.x

import ReactDOM from 'react-dom';
import {Provider} from '@shopify/app-bridge-react';
function MyApp() {
return (
<Provider config={config}>
<div>My app</div>
</Provider>
);
}
const root = document.createElement('div');
document.body.appendChild(root);
ReactDOM.createRoot(root).render(<MyApp />);

4.x.x

import ReactDOM from 'react-dom';
function MyApp() {
return (
<div>My app</div>
);
}
const root = document.createElement('div');
document.body.appendChild(root);
ReactDOM.createRoot(root).render(<MyApp />);

Anchor to Step 4: Update componentsStep 4: Update components

The following components and props have been refactored, renamed, or removed. Review and update the following components as described:

The Modal component allows you to display an overlay that prevents interaction with the rest of the app until dismissed. The following updates have been made to the Modal component's behaviour and props.

The size prop has been renamed variant to more accurately describe it's purpose. The variant prop accepts plain strings rather than constants.

size > variant

3.x.x

<Modal size={Modal.Size.Large} />

4.x.x

<Modal variant='large'></Modal>
Note

The Modal component has a new variant called max. Refer to the max modal example for more details.

The Modal component no longer accepts a message prop. Instead, it now accepts React elements as children.

message prop removed

3.x.x

<Modal message="Hello world!" />

4.x.x

<Modal>
<p>Hello world!</p>
</Modal>

The Modal component no longer accepts a title prop, Instead, now uses the TitleBar component to control the modal title.

title prop removed

3.x.x

<Modal title="My modal" />

4.x.x

<Modal>
<TitleBar title="My modal" />
</Modal>

The onClose prop has been renamed to onHide in the new Modal component to standardize the props with the admin extensions components library.

onClose > onHide

3.x.x

<Modal onClose={handleClose} />

4.x.x

<Modal onHide={handleClose}></Modal>

Anchor to [object Object], and ,[object Object], props removedprimaryAction and secondaryActions props removed

The Modal component no longer accepts the primaryAction and secondaryActions props. It now uses the TitleBar component to control the modal action buttons.

A TitleBar component in a Modal component accepts only one secondary action.

primaryAction and secondaryActions props removed

3.x.x

<Modal
primaryAction={{
content: 'Add Instagram',
onAction: handleChange
}}
secondaryActions={[
{
content: 'Learn more',
onAction: handleChange,
},
]}
/>

4.x.x

<Modal>
<TitleBar>
<button variant='primary' onClick={handleChange}>Add Instagram</button>
<button onClick={handleChange}>Learn more</button>
</TitleBar>
</Modal>

Anchor to [object Object], utilitysetupModalAutoSizing utility

In previous versions, you needed to set up modal auto-sizing to allow your modal to update its height to fit the page content. This is no longer needed, because auto-sizing is set automatically.

Anchor to Communication with the parent pageCommunication with the parent page

In previous versions, you needed to use the Modal.Action.DATA action to communicate between the parent page and the modal. There are now two ways to communicate between the modal and the parent page.

If you're using a modal with custom content, you can use the Broadcast Channel Web API to communicate with the parent page.

Communication with the parent page

3.x.x

function MyModal() {
return (
<Modal title='Hello world!' src="/modal-route" />
)
}

// component rendered at /modal-route
function ModalRouteComponent() {
const app = useAppBridge();

const sendMessageToApp = () => {
app.dispatch(Modal.data({message: 'Hi, this is the modal!'}));
}

return (
<div>
<button onClick={sendMessageToApp}>Send message</button>
</div>
)
}

function MyApp() {
const app = useAppBridge();

useEffect(() => {
app.subscribe(Modal.Action.DATA, (data) => {
console.log('Received message from modal: ', data.message);
});
}, []);

return (
<div>My app</div>
)
}

4.x.x

const modalChannel = new BroadcastChannel('my-modal');

function MyModal() {
const sendMessageToApp = () => {
modalChannel.postMessage('Hi, this is the modal!');
}

return (
<Modal>
<button onClick={sendMessageToApp}>Send message</button>
</Modal>
)
}

function MyApp() {
useEffect(() => {
modalChannel.addEventListener('message', (event) => {
console.log('Received message from modal: ', event);
});
}, []);

return (
<div>My app</div>
)
}

If you're using a modal with a src prop, you can use the postMessage API to communicate with the parent page. You can access postMessage through the window.opener object in the modal and through the modal.contentWindow object in the parent page.

Communication with the parent page

3.x.x

function MyModal() {
return (
<Modal title='Hello world!' src="/modal-route" />
)
}

// component rendered at /modal-route
function ModalRouteComponent() {
const app = useAppBridge();

const sendMessageToApp = () => {
app.dispatch(Modal.data({message: 'Hi, this is the modal!'}));
}

return (
<div>
<button onClick={sendMessageToApp}>Send message</button>
</div>
)
}

function MyApp() {
const app = useAppBridge();

useEffect(() => {
app.subscribe(Modal.Action.DATA, (data) => {
console.log('Received message from modal: ', data.message);
});
}, []);

return (
<div>My app</div>
)
}

4.x.x

function MyModal() {
return (
<Modal id="my-modal" src="/modal-route">
<TitleBar title="Hello world!" />
</Modal>
)
}

// component rendered at /modal-route
function ModalRouteComponent() {
const app = useAppBridge();

useEffect(() => {
function handleMessageFromMainApp(ev) {
console.log('Message received in modal:', ev.data);
}

window.addEventListener('message', handleMessageFromMainApp)
return () => {
window.removeEventListener('message', handleMessageFromMainApp)
}
}, [])

const sendMessageToApp = () => {
window.opener.postMessage('Hi, this is the modal!', location.origin);
}

return (
<div>
<button onClick={sendMessageToApp}>Send message</button>
</div>
)
}

function MyApp() {
const app = useAppBridge();

useEffect(() => {
function handleMessageFromModal(ev) {
console.log('Message received in main app:', ev.data);
}

window.addEventListener('message', handleMessageFromModal)
return () => {
window.removeEventListener('message', handleMessageFromModal)
}
}, [])

const sendMessageToApp = () => {
const modal = document.getElementById('my-modal');
modal.contentWindow.postMessage('Hi, this is the main app!', location.origin);
}

return (
<div>My app</div>
)
}

The TitleBar component allows you to populate a standardized title bar with button actions and navigation breadcrumbs. It can now also be used inside a Modal component to control the title and footer content to reduce the number of APIs that you need to learn. The following updates have been made to the title bar component's behaviour and props.

Anchor to [object Object],, ,[object Object],, and ,[object Object], props removedprimaryAction, secondaryActions, and actionGroups props removed

The TitleBar component no longer accepts the primaryAction, secondaryActions, and actionGroups props. Instead, to provide title bar actions, add button elements as children.

primaryAction

3.x.x

<TitleBar
title='Hello world!'
primaryAction={{
content: 'Foo',
onAction: handleFooClick
}}
secondaryActions={[
{
content: 'Bar',
onAction: handleBarClick,
loading: true,
},
]}
actionGroups={[
{
title: 'More actions',
actions: [
{
content: 'First grouped button',
onAction: handleFirstGroupedButtonClick,
},
{
content: 'Second grouped button',
onAction: handleSecondGroupedButtonClick,
disabled: true,
}
]
}
]}
/>

4.x.x

<TitleBar title='Hello world!'>
<button variant='primary' onClick={handleFooClick}>Foo</button>
<button onClick={handleBarClick}>Bar</button>
<section label='More actions'>
<button onClick={handleFirstGroupedButtonClick}>First grouped button</button>
<button onClick={handleSecondGroupedButtonClick} disabled>Second grouped button</button>
</section>
</TitleBar>

The TitleBar component no longer accepts the breadcrumbs prop. Instead, a breadcrumb can be added to the title bar using a child a or button element with the variant="breadcrumb" attribute set.

breadcrumbs prop removed

3.x.x

<TitleBar
title='Hello world!'
breadcrumbs={[
{
content: 'Breadcrumb',
url: '/breadcrumb',
}
]}
/>

4.x.x

<TitleBar title='Hello world!'>
<a variant='breadcrumb' href='/breadcrumb'>Breadcrumb</a>
</TitleBar>

The NavMenu component creates a navigation menu for your app. This component was called NavigationMenu in previous versions of Shopify App Bridge. It was renamed to NavMenu to more closely match the HTML nav element. The following updates have been made to the navigation menu component's behaviour and props.

The navigation menu component no longer accepts the navigationLinks and matcher props. Instead, to provide menu links, add a elements as children of the NavMenu component. The active link is automatically matched.

The first element of the NavMenu component is required and used to configure the home route of the app. It is not rendered as a link. It needs to have rel="home" set along with the href set to the root path of your app.

navigationLinks and matcher props removed

3.x.x

<NavigationMenu
navigationLinks={[
{
label: 'Home',
destination: '/',
},
{
label: 'Templates',
destination: '/templates',
},
{
label: 'Settings',
destination: '/settings',
},
]}
matcher={(link, location) => link.destination === location.pathname}
/>

4.x.x

<NavMenu>
<a href="/" rel="home">Home</a>
<a href="/templates">Templates</a>
<a href="/settings">Settings</a>
</NavMenu>

All other React components from previous versions have been removed, and replaced by new APIs provided through the shopify global variable. Refer to the following table to learn about the API replacement for each component:

| Component | New API | |---|---|---| | ContextualSaveBar | Contextual Save Bar API | | Loading | Loading API | | unstable_Picker | Picker API | | ResourcePicker | ResourcePicker API | | Toast | Toast API |


Anchor to Step 5: Update hooksStep 5: Update hooks

The following hooks have been refactored, renamed, or removed. Review and update the following components as described:

The useAppBridge hook included in the most recent version of Shopify App Bridge functions differently than the previous useAppBridge hook. Instead of returning the App Bridge app instance, it returns the shopify global variable, which is used to access App Bridge APIs such as toast and resourcePicker. As the shopify variable is only available in a browser context, the useAppBridge hook throws helpful error messages when used in a server context or in a misconfigured app.

You no longer need to use the app reference returned from old uses of the useAppBridge hook as App Bridge handles configuration for you.

4.x.x

function GenerateBlogPostButton() {
const shopify = useAppBridge();

function generateBlogPost() {
// Handle generating
shopify.toast.show('Blog post template generated');
}

return <button onClick={generateBlogPost}>Generate Blog Post</button>;
}

All other React hooks from previous versions have been removed, and replaced by new APIs provided through the shopify global variable. In most cases, you can use the useAppBridge hook in place of these hooks, and access the corresponding API through the returned shopify global variable.

Refer to the following table to learn about the API replacement for each hook:

HookReplacement
useAppBridgeStateThis functionality has been spread out across a few APIs. For example, to retrieve the current staffMember, use the User API.
useAuthenticatedFetchThis hook is no longer needed, because App Bridge injects automatic authorization into the global fetch function. For more information, refer to Resource Fetching.
useContextualSaveBarThe contextual save bar is now automatically configured when you provide the data-save-bar attribute to a form element. For more information, refer to Contextual Save Bar.
useNavigateNavigation API
useNavigationHistoryNavigation API
useToastToast API

Was this page helpful?