Shopify App Frontends
Gadget provides rich tools for building frontends for Shopify applications, including apps embedded in the Shopify admin using the Shopify App Bridge. Gadget's @gadgetinc/react-shopify-app-bridge
implements Shopify's requirements for OAuth flows, secure iframe embedding, and Session Token authentication out of the box so you can focus on building your app.
Frontends for Shopify Apps communicate with Gadget backends using your Gadget app's GraphQL API and the associated JS client for your application. There are a few different npm packages necessary for working with Gadget from a Shopify app frontend:
Package | Description | Available from |
---|---|---|
@shopify/app-bridge | Shopify's React package for embedding React applications within the Shopify Admin | npm |
@gadget-client/example-app | The JS client for your specific Gadget application | Gadget NPM registry |
@gadgetinc/react | The Gadget React bindings library, providing React hooks for making API calls | npm |
@gadgetinc/react-shopify-app-bridge | The Gadget Shopify wrapper library for Shopify Embedded App setup and authentication | npm |
Installation
To build a Shopify App frontend with your Gadget backend, you must first set up a React application. There are a few options for setting up a React app:
create-react-app
andNext.js
, see the Building Frontends guide- from the Shopify CLI's template app, see the Connecting to Shopify via CLI guide
Gadget recommends the Next.js
approach as it is the easiest to get started with and includes a lot of great functionality out of the box.
After creating a React app, you must install your Gadget application's JS client package.
To install the Example App node package, register the Gadget NPM registry for the @gadget-client
package scope:
npm config set @gadget-client:registry https://registry.gadget.dev/npm
Then, install the Example App client package and the @gadgetinc/react
package using your package manager:
npm install @gadget-client/example-app @gadgetinc/react
yarn add @gadget-client/example-app @gadgetinc/react
These instructions are examples for an example app. When you create your own Gadget app, they'll update to reflect the right name and commands for your specific application.
Create your own app at gadget.new
As you make changes to Example App, Gadget will publish new versions of your API client to its NPM registry. See Client Regeneration for more details on when updates are necessary.
Next, you must install Shopify's App Bridge package (@shopify/app-bridge
), and Gadget's Shopify bindings package (@gadgetinc/react-shopify-app-bridge
).
npm install --save @gadgetinc/react-shopify-app-bridge @gadgetinc/react @shopify/app-bridge-react
yarn add @gadgetinc/react-shopify-app-bridge @gadgetinc/react @shopify/app-bridge-react
If you are using the Shopify CLI 3.0 to create your app, you need to install your Gadget dependencies in the /web/frontend
directory!
The Provider
The @gadgetinc/react-shopify-app-bridge
library handles authentication of your embedded app via the Provider
component. This provider has two main benefits - it handles authentication and the series of redirects required to complete an embedded app OAuth flow in Shopify, and it handles retrieving a Shopify session token from the App Bridge and passing it along to Gadget for authenticated calls.
The Provider handles these key tasks automatically:
- 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 Shopify's Session Token authentication scheme
- sets up the correct React context for making backend calls to Gadget using
@gadgetinc/react
The Provider
has the following required props:
export interface ProviderProps {type: AppType; // 'AppType.Embedded' or 'AppType.Standalone'shopifyApiKey: string; // the API key from your Shopify app in the partner dashboard that is used with the Shopify App Bridgeapi: string; // the API client created using your Gadget application}
The Gadget provider will handle detecting if your app is being rendered in an embedded context and redirect the user through Shopify's OAuth flow if necessary.
Provider
example
Start by setting up your API client. You can instantiate the client without any options. The Provider
will set up authentication with Shopify using Shopify Session Tokens when you pass the api
instance to it later.
import { Client } from "@gadget-client/my-gadget-app";export const api = new Client();
Now we need to import and set up the Provider
in the App component:
Once the Provider
is set up properly, you can start to build out components in your embedded application.
1import {2 AppType,3 Provider as GadgetProvider,4} from "@gadgetinc/react-shopify-app-bridge";5import { api } from "./api";6import { ProductManager } from "./productManager";7import React from "react";89export function MyApp() {10 return (11 // type can be omitted. Defaults to AppType.Embedded12 <GadgetProvider type={AppType.Embedded} shopifyApiKey={apiKey} api={api}>13 <ProductManager />14 </GadgetProvider>15 );16}
Shopify App Setup
When building embedded apps for Shopify, the App URL field in the Shopify App setup (found in your Partners Dashboard) should be set to the URL where your frontend application is hosted. For example, if you are developing locally this would likely be set to https://localhost
. Additionally, the App URL in your Gadget app's Shopify Connection should be set to the same URL.
Note: Shopify expects a Content-Security-Policy
header to be set from your application for it to be embedded in the
Shopify Admin. The header value should be set to frame-ancestors https://example-shop.myshopify.com/ https://admin.shopify.com;
The useGadget
React hook
The Provider
handles initializing the App Bridge for us. Now we can build our application component and use the initialized instance of App Bridge via the appBridge
key returned from the embedded React hook useGadget
.
useGadget
provides the following properties:
1export interface useGadget {2 isAuthenticated: boolean; // 'true' if the user has completed a successful OAuth flow3 isEmbedded: boolean; // 'true' if the app is running in an embedded context4 isRootFrameRequest: boolean; // 'true' if a user is viewing a "type: AppType.Embedded" app in a non-embedded context, for example, accessing the app at a hosted Vercel domain5 loading: boolean; // 'true' if the OAuth flow is in process6 appBridge: AppBridge; // a ready-to-use app bridge from Shopify, you can also use the traditional useAppBridge hook in your components to retrieve it.7}
useGadget
example
The following example renders a ProductManager component that makes use of Shopify App Bridge components and is ready to be embedded in a Shopify Admin page.
1import { useAction, useFindMany } from "@gadgetinc/react";2import { useGadget } from "@gadgetinc/react-shopify-app-bridge";3import { Button, Redirect, TitleBar } from "@shopify/app-bridge/actions";4import { api } from "./api.ts";56function ProductManager() {7 const { loading, appBridge, isRootFrameRequest } = useGadget();8 const [_, deleteProduct] = useAction(api.shopifyProduct.delete);9 const [{ data, fetching, error }, refresh] = useFindMany(api.shopifyProduct);1011 if (error) return <>Error: {error.toString()}</>;12 if (fetching) return <>Fetching...</>;13 if (!data) return <>No widgets found</>;1415 // Set up a title bar for the embedded app16 const breadcrumb = Button.create(appBridge, { label: "My breadcrumb" });17 breadcrumb.subscribe(Button.Action.CLICK, () => {18 appBridge.dispatch(Redirect.toApp({ path: "/breadcrumb-link" }));19 });2021 const titleBarOptions = {22 title: "My page title",23 breadcrumbs: breadcrumb,24 };25 TitleBar.create(appBridge, titleBarOptions);2627 return (28 <>29 {loading && <span>Loading...</span>}30 {isRootFrameRequest && (31 <span>App can only be viewed in the Shopify Admin!</span>32 )}33 {!loading &&34 !isRootFrameRequest &&35 data.map((widget, i) => (36 <button37 key={i}38 onClick={(event) => {39 event.preventDefault();40 void deleteProduct({ id: widget.id }).then(() => refresh());41 }}42 >43 Delete {widget.title}44 </button>45 ))}46 </>47 );48}
GraphQL Queries
When building embedded Shopify apps, there may be instances where a Shop's installed scopes have not been updated to match the required scopes in your Gadget app's Connection. In these situations, it is necessary to re-authenticate with Shopify so that the app can acquire the updated scopes. The following GraphQL query can be run using the app client (passed to the Provider
) and provides information related to missing scopes and whether a re-authentication is necessary.
1query {2 shopifyConnection {3 requiresReauthentication4 missingScopes5 }6}
Session model management
The Shopify Connection in Gadget automatically manages records of the backend Session model when using @gadgetinc/react-shopify-app-bridge
. When a merchant first loads up the front-end application, the <Provider/>
will retrieve a Shopify Session Token from Shopify's API, and pass it to your Gadget backend application. The Gadget Shopify Connection will then validate this token. If valid, the connection will provision a new record of the Session model with the correct shopId
field set up. This session is then passed to all your backend application's model filters and available within Action code snippets.
Embedded app examples
Want to see an example of an embedded Shopify app built using Gadget?
Check out some of our example apps on GitHub, including: