@gadgetinc/react-shopify-app-bridge 

This package provides React hooks and components for use with Shopify embedded app development with Gadget.

Features 

For Shopify app development, setting up and understanding the Shopify App Bridge is essential.

This package significantly simplifies this process by automating the OAuth authentication and App Bridge initialization by handling the following:

  • Automatically starts the OAuth process with new users of the application using Gadget, escaping Shopify's iframe if necessary.
  • Establishes an iframe-safe secure session with the Gadget backend using Gadget OAuth PKCE.
  • Sets up the correct React context for making backend calls to Gadget using @gadgetinc/react.

This package exports:

Installation 

If you select the Shopify template upon app creation in Gadget, @gadgetinc/react-shopify-app-bridge is already installed.

This is a companion package to the JavaScript client package generated for your Gadget app. You must first install the client for your app, and then install this package.

To install the client for your app, you must set up the Gadget NPM registry:

Register the Gadget npm registry
yarn config set @gadget-client:registry https://registry.gadget.dev/npm
npm config set @gadget-client:registry https://registry.gadget.dev/npm

and then install your app's API client:

Install required packages
yarn add @gadget-client/example-app
npm install @gadget-client/example-app

Once you have your client installed, you can install the React hooks library and the Shopify App bridge library with yarn or npm:

Install required packages
yarn add @gadgetinc/react-shopify-app-bridge @gadgetinc/react @shopify/app-bridge-react react
npm install --save @gadgetinc/react-shopify-app-bridge @gadgetinc/react @shopify/app-bridge-react react

Usage 

In order to make use of App Bridge, you need to:

  • Set up your API client
  • Handle Shopify OAuth and App Bridge setup with the Provider
  • Get an App Bridge instance to use with the useGadget() hook

API client setup 

Here is how your API client is typically set up in your Gadget frontend:

src/api.js
JavaScript
import { Client } from "@gadget-client/example-app";
export const api = new Client();
import { Client } from "@gadget-client/example-app";
export const api = new Client();

Using Provider and useGadget to set up App Bridge 

Here is an example of how to set up App Bridge, and use it to open an App Bridge resource picker:

JavaScript
1// import the Gadget<->Shopify bindings that manage the auth process with Shopify
2import {
3 AppType,
4 Provider as GadgetProvider,
5 useGadget,
6} from "@gadgetinc/react-shopify-app-bridge";
7// import the instance of the Gadget API client for this app constructed in the other file
8import { api } from "../api";
9// import the useGlobalAction hook to help call your Gadget API
10import { useGlobalAction } from "@gadgetinc/react";
11
12export default function App() {
13 return (
14 // Wrap our main application's react components in the `<GadgetProvider/>` component
15 // to interface with Shopify. This wrapper sets up the Shopify App Bridge.
16 // It will automatically redirect to perform the OAuth authentication
17 // if the shopify shop doesn't yet have the store installed.
18 <GadgetProvider
19 type={AppType.Embedded}
20 shopifyApiKey={window.gadgetConfig.apiKeys.shopify}
21 api={api}
22 >
23 <SimplePage />
24 </GadgetProvider>
25 );
26}
27
28// An example component that uses the Gadget React hooks to work with data in the backend
29function SimplePage() {
30 const { loading, appBridge, isRootFrameRequest, isAuthenticated } = useGadget();
31
32 // makeSelections is a global action in this example
33 // it is called using Gadget's useGlobalAction hook
34 const [{ error }, makeSelections] = useGlobalAction(api.makeSelections);
35
36 return (
37 <>
38 {loading && <span>Loading...</span>}
39 {/* A user is viewing this page from a direct link so show them the home page! */}
40 {!loading && isRootFrameRequest && (
41 <div>Welcome to my cool app's webpage!</div>
42 )}
43 {error && <div>{error.message}</div>}
44 {!loading && isAuthenticated && (
45 <button
46 onClick={async () => {
47 if (appBridge) {
48 // open the App Bridge resource picker component
49 const selected = appBridge.resourcePicker({ type: "product" });
50 console.log({ selected });
51 // pass selections to a global action
52 await makeSelections({ selected });
53 }
54 }}
55 >
56 Open resource picker
57 </button>
58 )}
59 </>
60 );
61}
1// import the Gadget<->Shopify bindings that manage the auth process with Shopify
2import {
3 AppType,
4 Provider as GadgetProvider,
5 useGadget,
6} from "@gadgetinc/react-shopify-app-bridge";
7// import the instance of the Gadget API client for this app constructed in the other file
8import { api } from "../api";
9// import the useGlobalAction hook to help call your Gadget API
10import { useGlobalAction } from "@gadgetinc/react";
11
12export default function App() {
13 return (
14 // Wrap our main application's react components in the `<GadgetProvider/>` component
15 // to interface with Shopify. This wrapper sets up the Shopify App Bridge.
16 // It will automatically redirect to perform the OAuth authentication
17 // if the shopify shop doesn't yet have the store installed.
18 <GadgetProvider
19 type={AppType.Embedded}
20 shopifyApiKey={window.gadgetConfig.apiKeys.shopify}
21 api={api}
22 >
23 <SimplePage />
24 </GadgetProvider>
25 );
26}
27
28// An example component that uses the Gadget React hooks to work with data in the backend
29function SimplePage() {
30 const { loading, appBridge, isRootFrameRequest, isAuthenticated } = useGadget();
31
32 // makeSelections is a global action in this example
33 // it is called using Gadget's useGlobalAction hook
34 const [{ error }, makeSelections] = useGlobalAction(api.makeSelections);
35
36 return (
37 <>
38 {loading && <span>Loading...</span>}
39 {/* A user is viewing this page from a direct link so show them the home page! */}
40 {!loading && isRootFrameRequest && (
41 <div>Welcome to my cool app's webpage!</div>
42 )}
43 {error && <div>{error.message}</div>}
44 {!loading && isAuthenticated && (
45 <button
46 onClick={async () => {
47 if (appBridge) {
48 // open the App Bridge resource picker component
49 const selected = appBridge.resourcePicker({ type: "product" });
50 console.log({ selected });
51 // pass selections to a global action
52 await makeSelections({ selected });
53 }
54 }}
55 >
56 Open resource picker
57 </button>
58 )}
59 </>
60 );
61}

Once isAuthenticated is true, you will be able to make authenticated API requests using your Gadget API client and the @gadgetinc/react hooks.

The Provider and useGadget are already set up for you when you connect to Shopify using Gadget's frontends.

Custom router 

@shopify/app-bridge-react allows you to specify a custom router configuration to manage client-side routing. Similarly, the Gadget provider will allow you to specify a custom router which will be forwarded to the App Bridge.

JavaScript
1import {
2 AppType,
3 Provider as GadgetProvider,
4 useGadget,
5} from "@gadgetinc/react-shopify-app-bridge";
6import { NavMenu } from "@shopify/app-bridge-react";
7import { useEffect } from "react";
8import {
9 Link,
10 Outlet,
11 Route,
12 RouterProvider,
13 createBrowserRouter,
14 createRoutesFromElements,
15 useLocation,
16 useNavigate,
17} from "react-router";
18import { api } from "../api";
19import Index from "../routes/index";
20
21function Error404() {
22 // use navigation hooks to handle 404 pages for undefined routes
23 const navigate = useNavigate();
24 const location = useLocation();
25
26 useEffect(() => {
27 const appURL = process.env.GADGET_PUBLIC_SHOPIFY_APP_URL;
28
29 if (appURL && location.pathname === new URL(appURL).pathname) {
30 navigate("/", { replace: true });
31 }
32 }, [location.pathname]);
33
34 return <div>404 not found</div>;
35}
36
37function App() {
38 // define routes
39 const router = createBrowserRouter(
40 createRoutesFromElements(
41 <Route path="/" element={<Layout />}>
42 <Route index element={<Index />} />
43 <Route path="*" element={<Error404 />} />
44 </Route>
45 )
46 );
47
48 // and return the router provider that defines the App component
49 return (
50 <>
51 <RouterProvider router={router} />
52 </>
53 );
54}
55
56function Layout() {
57 // use the Provider to handle auth, init App Bridge
58 return (
59 <GadgetProvider
60 type={AppType.Embedded}
61 shopifyApiKey={window.gadgetConfig.apiKeys.shopify}
62 api={api}
63 >
64 <AuthenticatedApp />
65 </GadgetProvider>
66 );
67}
68
69function AuthenticatedApp() {
70 // we use `isAuthenticated` to render pages once the OAuth flow is complete!
71 const { isAuthenticated, loading } = useGadget();
72 if (loading) {
73 return <>Loading...</>;
74 }
75 return isAuthenticated ? <EmbeddedApp /> : <UnauthenticatedApp />;
76}
77
78function EmbeddedApp() {
79 // use the Outlet, which renders the correct page given the current route
80 // and use the App Bridge NavMenu for Shopify embedded app nav
81 return (
82 <>
83 <Outlet />
84 <NavMenu>
85 <Link to="/" rel="home">
86 Shop Information
87 </Link>
88 </NavMenu>
89 </>
90 );
91}
92
93function UnauthenticatedApp() {
94 return <>App must be viewed in the Shopify Admin</>;
95}
96
97export default App;
1import {
2 AppType,
3 Provider as GadgetProvider,
4 useGadget,
5} from "@gadgetinc/react-shopify-app-bridge";
6import { NavMenu } from "@shopify/app-bridge-react";
7import { useEffect } from "react";
8import {
9 Link,
10 Outlet,
11 Route,
12 RouterProvider,
13 createBrowserRouter,
14 createRoutesFromElements,
15 useLocation,
16 useNavigate,
17} from "react-router";
18import { api } from "../api";
19import Index from "../routes/index";
20
21function Error404() {
22 // use navigation hooks to handle 404 pages for undefined routes
23 const navigate = useNavigate();
24 const location = useLocation();
25
26 useEffect(() => {
27 const appURL = process.env.GADGET_PUBLIC_SHOPIFY_APP_URL;
28
29 if (appURL && location.pathname === new URL(appURL).pathname) {
30 navigate("/", { replace: true });
31 }
32 }, [location.pathname]);
33
34 return <div>404 not found</div>;
35}
36
37function App() {
38 // define routes
39 const router = createBrowserRouter(
40 createRoutesFromElements(
41 <Route path="/" element={<Layout />}>
42 <Route index element={<Index />} />
43 <Route path="*" element={<Error404 />} />
44 </Route>
45 )
46 );
47
48 // and return the router provider that defines the App component
49 return (
50 <>
51 <RouterProvider router={router} />
52 </>
53 );
54}
55
56function Layout() {
57 // use the Provider to handle auth, init App Bridge
58 return (
59 <GadgetProvider
60 type={AppType.Embedded}
61 shopifyApiKey={window.gadgetConfig.apiKeys.shopify}
62 api={api}
63 >
64 <AuthenticatedApp />
65 </GadgetProvider>
66 );
67}
68
69function AuthenticatedApp() {
70 // we use `isAuthenticated` to render pages once the OAuth flow is complete!
71 const { isAuthenticated, loading } = useGadget();
72 if (loading) {
73 return <>Loading...</>;
74 }
75 return isAuthenticated ? <EmbeddedApp /> : <UnauthenticatedApp />;
76}
77
78function EmbeddedApp() {
79 // use the Outlet, which renders the correct page given the current route
80 // and use the App Bridge NavMenu for Shopify embedded app nav
81 return (
82 <>
83 <Outlet />
84 <NavMenu>
85 <Link to="/" rel="home">
86 Shop Information
87 </Link>
88 </NavMenu>
89 </>
90 );
91}
92
93function UnauthenticatedApp() {
94 return <>App must be viewed in the Shopify Admin</>;
95}
96
97export default App;

Creating a new Shopify app in Gadget will set up custom routing for you using either react-router or Remix. Remix apps will use file-based routing.

Breaking changes when upgrading to version 0.14.0 

Because @gadgetinc/react-shopify-app-bridge version 0.14.0 supports App Bridge V4, this requires you to migrate to your app from App Bridge V3.

Otherwise, you will experience many breaking changes to your app, this also means you will have to update any hooks or components that aren't supported by V4. To upgrade your Gadget application to support App Bridge V4, check out our guide here.

Reference 

Provider 

A Provider component that wraps your Shopify embedded app to authenticate the API client and generate a session token.

Props 
  • api: Client - The Gadget API client to use for the extension.
  • shopifyApiKey: string - The API key for the Shopify Partner app.
  • type: AppType - Either AppType.Embedded which is the default when setting up a Shopify connection in Gadget, or AppType.Standalone for non-embedded apps.

useGadget() 

useGadget(): { loading: boolean, isAuthenticated: boolean }

A React hook that determines when the OAuth request is underway and if the user is authenticated.

Returns 
  • loading: boolean - A boolean that is true when the OAuth request is in progress.
  • canAuth: boolean - A boolean that is true if the current browser window is ready to authenticate.
  • isAuthenticated: boolean - A boolean that is true when the user is authenticated.
  • appBridge: ShopifyGlobal | null - An instance of the App Bridge object. Only available when the appType is Embedded.
  • isEmbedded: boolean - A boolean that is true when the app is running inside the iframe in the Shopify admin.
  • isRootFrameRequest: boolean - A boolean that is true if the app is being rendered outside of the Shopify admin flow. Only applies when appType is Embedded.

Was this page helpful?