Building Shopify app extensions 

Working with Admin and Checkout UI extensions 

Setting up your extension app from scratch 

  1. In your local terminal run the following command to create a new Shopify CLI app:
terminal
npm init @shopify/app@latest
yarn create @shopify/app
  1. Select the Start by adding your first extension option.
  2. Upon app creation open the generated app folder and cd into the app's root.
  3. Then run the following command to generate your extension.
terminal
npm run generate extension
yarn generate extension
  1. Select Yes, create it as a new app
  2. Proceed to name your app.
  3. You will then be prompted to select the type of admin extension or checkout UI extension you wish to build and to name it.
  4. After the extension code is generated open your shopify.app.toml file, locate the access_scopes section and add the following to it:
shopify.app.toml
[access_scopes]
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
scopes = ""
use_legacy_install_flow = true
  1. After updating the TOML file, push the configuration changes to your partner's app using the following command:
terminal
shopify app config push
  1. Finally head over to Gadget, create a new Shopify app and connect to Shopify (instead of creating a manual app from the Partners dashboard you will use the app you created earlier through the CLI).

Setting up your extension with an existing Shopify connected Gadget app 

Alternatively if you already have Gadget app with an established Shopify connection you can proceed to create a new Shopify CLI app and select the Start by adding your first extension option.

Follow along with the rest of the extension setup, but make sure as prompted by the CLI you link your extension to the same Partners app you used to set up your Shopify connection in Gadget, and select the same development store you installed your app on.

Accessing your Gadget backend data from the extension 

How Auth is handled in Admin extensions 

Gadget backends automatically support authenticating requests made from within Admin Extensions to your Gadget backend.

Shopify automatically adds an Authorization header to requests sent by Admin Extensions containing a Shopify Session Token. Similar to embedded applications authenticating against your Gadget backend, Gadget will parse the token contained in this header and set up a record in your session model representing a given merchant, as well as set up the connections.shopify object for communicating with with Shopify Admin API.

Using the API client in Extensions 

We recommend using our API client within the extension to get data access as it is the easiest way to get started:

  1. cd into your extension code's directory.
  2. Install the client

    .
  3. Once your client is installed, you must provide an instance of your client using the Gadget React provider.
  4. Import the api instance and the provider, and wrap the provider around the reactExtension code as below:
Checkout.jsx
JavaScript
1import { Banner, reactExtension } from "@shopify/ui-extensions-react/checkout";
2import { Provider } from "@gadgetinc/react"; /// Imported provider
3import { api } from "../api"; // Importing the instance created
4
5export default reactExtension("purchase.checkout.block.render", () => (
6 <Provider api={api}>
7 {/* Wrapping the extension around the Gadget provider and passing the api instance */}
8 <Extension />
9 </Provider>
10));
11
12function Extension() {
13 return <Banner>This is a product.</Banner>;
14}
  1. Now your extension has full access to the Gadget API and all the react hooks, so below will simply get data from our Gadget backend using a useFindOne hook to grab the title of our shopifyProduct model.
Checkout.jsx
JavaScript
1import { Banner, reactExtension } from "@shopify/ui-extensions-react/checkout";
2import { Provider, useFindOne } from "@gadgetinc/react";
3import { api } from "../api";
4
5export default reactExtension("purchase.checkout.block.render", () => (
6 <Provider api={api}>
7 <Extension />
8 </Provider>
9));
10
11function Extension() {
12 const [{ data, error, fetching }, refresh] = useFindOne(api.shopifyProduct, "123");
13
14 if (fetching) {
15 return <Banner>Loading...</Banner>;
16 }
17
18 if (error) {
19 return <Banner>Error loading product. Please try again.</Banner>;
20 }
21
22 return <Banner>{data.title}</Banner>;
23}

HTTP request using the GraphQL endpoint 

Alternatively as a fallback option you can also make a request directly to fetch from your GraphQl API endpoint.

Gadget supports authenticating requests made from Shopify Checkout Extensions using Shopify's Session Token feature. Inside a checkout extension that has the network_access capability, you can make a request to your backend Gadget API or an HTTP route:

Checkout.jsx
JavaScript
1import {
2 Banner,
3 reactExtension,
4 useApi,
5} from "@shopify/ui-extensions-react/checkout";
6import { useState, useEffect } from "react";
7
8export default reactExtension("purchase.checkout.block.render", () => <Extension />);
9
10function Extension() {
11 // Shopify's Session Token
12 const { sessionToken } = useApi();
13 const [productData, setProductData] = useState(null);
14
15 useEffect(() => {
16 // Specify the GraphQL endpoint
17 const url = "https://my-extension-app-development.gadget.dev/api/graphql";
18
19 // Create a GraphQL query
20 const query = `
21 query GetOneShopifyProduct($id: GadgetID!) {
22 shopifyProduct(id: $id) {
23 title
24 }
25 }
26 `;
27
28 // Fetching the Token
29 async function getToken() {
30 const token = await sessionToken.get();
31 return token;
32 }
33
34 // fetch and make a POST request to the GraphQL endpoint
35 getToken().then((token) => {
36 fetch(url, {
37 method: "POST",
38 headers: {
39 "Content-Type": "application/json",
40 Accept: "application/json",
41 Authorization: `Bearer ${token}`,
42 },
43 body: JSON.stringify({ query: query }),
44 })
45 .then((response) => response.json())
46 .then((data) => {
47 setProductData(data.data.product);
48 })
49 .catch((error) => console.error("Error:", error));
50 });
51 }, []);
52
53 return <Banner>{productData.title}</Banner>;
54}

Using Metafields 

Another option which is a bit lengthier involves querying and writing metafields to pass and set data within extension.

For more information checkout our metafields guide.