# `@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](https://shopify.dev/docs/api/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`](https://docs.gadget.dev/reference/react).
This package exports:
* that handles OAuth and App Bridge initialization
* that will return an initialized App Bridge instance
## 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:
```yarn
yarn config set @gadget-client:registry https://registry.gadget.dev/npm
```
```npm
npm config set @gadget-client:registry https://registry.gadget.dev/npm
```
and then install your app's API client:
```yarn
yarn add @gadget-client/example-app
```
```npm
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:
```yarn
yarn add @gadgetinc/react-shopify-app-bridge @gadgetinc/react @shopify/app-bridge-react react
```
```npm
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:
```typescript
import { ExampleAppClient } from "@gadget-client/example-app";
export const api = new ExampleAppClient();
```
### 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:
```typescript
// import the Gadget<->Shopify bindings that manage the auth process with Shopify
import {
AppType,
Provider as GadgetProvider,
useGadget,
} from "@gadgetinc/react-shopify-app-bridge";
// import the instance of the Gadget API client for this app constructed in the other file
import { api } from "../api";
// import the useGlobalAction hook to help call your Gadget API
import { useGlobalAction } from "@gadgetinc/react";
export default function App() {
return (
// Wrap our main application's react components in the `` component
// to interface with Shopify. This wrapper sets up the Shopify App Bridge.
// It will automatically redirect to perform the OAuth authentication
// if the shopify shop doesn't yet have the store installed.
);
}
// An example component that uses the Gadget React hooks to work with data in the backend
function SimplePage() {
const { loading, appBridge, isRootFrameRequest, isAuthenticated } = useGadget();
// makeSelections is a global action in this example
// it is called using Gadget's useGlobalAction hook
const [{ error }, makeSelections] = useGlobalAction(api.makeSelections);
return (
<>
{loading && Loading...}
{/* A user is viewing this page from a direct link so show them the home page! */}
{!loading && isRootFrameRequest && (
Welcome to my cool app's webpage!
)}
{error &&
{error.message}
}
{!loading && isAuthenticated && (
)}
>
);
}
```
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.
```typescript
import {
AppType,
Provider as GadgetProvider,
useGadget,
} from "@gadgetinc/react-shopify-app-bridge";
import { NavMenu } from "@shopify/app-bridge-react";
import { useEffect } from "react";
import {
Link,
Outlet,
Route,
RouterProvider,
createBrowserRouter,
createRoutesFromElements,
useLocation,
useNavigate,
} from "react-router";
import { api } from "../api";
import Index from "../routes/index";
function Error404() {
// use navigation hooks to handle 404 pages for undefined routes
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
const appURL = process.env.GADGET_PUBLIC_SHOPIFY_APP_URL;
if (appURL && location.pathname === new URL(appURL).pathname) {
navigate("/", { replace: true });
}
}, [location.pathname]);
return
404 not found
;
}
function App() {
// define routes
const router = createBrowserRouter(
createRoutesFromElements(
}>
} />
} />
)
);
// and return the router provider that defines the App component
return (
<>
>
);
}
function Layout() {
// use the Provider to handle auth, init App Bridge
return (
);
}
function AuthenticatedApp() {
// we use `isAuthenticated` to render pages once the OAuth flow is complete!
const { isAuthenticated, loading } = useGadget();
if (loading) {
return <>Loading...>;
}
return isAuthenticated ? : ;
}
function EmbeddedApp() {
// use the Outlet, which renders the correct page given the current route
// and use the App Bridge NavMenu for Shopify embedded app nav
return (
<>
Shop Information
>
);
}
function UnauthenticatedApp() {
return <>App must be viewed in the Shopify Admin>;
}
export 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](https://docs.gadget.dev/guides/plugins/shopify/frontends#shopify-app-bridge-v4-support).
## 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 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`.