You can build your Shopify app extensions inside your Gadget app using the Shopify CLI and ggt, Gadget's CLI.
In your local terminal, run the ggt dev command replacing <YOUR APP DOMAIN> to pull down your app to your local machine:
terminal
ggt dev ./<YOUR APP DOMAIN> --app=<YOUR APP DOMAIN> --env=development
You can also click the cloud icon next to your environment selector in the Gadget editor to get your app's ggt dev command. See the
ggt guide for more info on working locally.
cd into your project, and open it in an editor.
Add the following workspaces and trustedDependencies to your package.json:
Once you add the workspaces definition to your package.json, you will need to use the -W flag to add new packages to your core Gadget app:
terminal
yarnadd -W <package>
This is required by Yarn workspaces to ensure that all packages are installed in the correct location.
Add a .ignore file to the root of your project.
Add the following to both .ignore (and .gitignore if you are using source control):
add to .ignore and .gitignore
extensions/*/dist
extensions/*/node_modules
If your Gadget app does not have a shopify.app.toml file, you need to manually add one to the root of your project. New Gadget apps will
come with a TOML file.
Use the Shopify CLI to generate your checkout UI extension:
terminal
shopify app generate extension
The following steps are for admin, checkout, or customer account extensions. For theme app extensions, see the theme app extensions section.
Select the same Partner app and development store you used to connect to Shopify when prompted by Shopify's CLI.
Select an extension type and a language for your extension.
This command will create an extensions folder at your project root, and your extension will be generated by the Shopify CLI.
Start your extension development server by running:
terminal
shopify app dev
Bringing an existing extension into Gadget?
If you are porting over an existing extension-only app and you are copying over your root-level app configuration shopify.app.toml, you
need to make sure use_legacy_install_flow = true is set in the [access_scopes]
section so Gadget can manage scope registration.
Using Shopify metafields as input
You can use Shopify metafields to store and retrieve custom data. This has the added benefit of being stored on Shopify's infrastructure, so you don't need to manage stored values in your Gadget database.
You do have the option to store metafield data in your Gadget database if it is required for your app. If you need access to metafield data in Gadget, you can add metadata fields to your Shopify data models.
Metafields are the only way to use custom data as input in some extensions, for example, most Shopify Functions.
Make a network request to your Gadget API
In some extensions, you can also send a request to your app's API to run custom backend code and return the data you need. This is useful if you need to run custom logic to generate the data you need.
Before you write any network requests, you'll need to set network_access = true in your extension's shopify.extension.toml file. Some extensions, such as Admin extensions, already allow you to make requests to your app backend, and don't require this setting.
Some extension types may not allow external network requests. Check Shopify's documentation for the extension type you're working with to see if network access is allowed.
Initialize your API client
To use your Gadget API client in an extension you can import your Client and initialize it with your current Gadget app environment:
exportconst api =newClient({environment: process.env["NODE_ENV"]});
If you are managing your extensions outside of your Gadget project, for example, in a Shopify CLI app, you need to
install your API client
.
Environment selection in extension clients
Shopify extensions are sandboxed, so there is not a simple way to get the current Gadget environment when starting an extension. If you only have a single development environment, using the extension's environment variable process.env.NODE_ENV could work for you.
If you have multiple development environments you will need a way to manually update the environment used to initialize the Client in extensions.
One option: add a small script file to your project that accepts an environment name and does string replacement for the environment used to init Client. Run this with a package.json script command that also starts the Shopify extension dev server. This approach can also work when deploying to production with a CI/CD pipeline.
Using @gadgetinc React hooks
The @gadgetinc/react hooks, such as useFindMany, useAction, and useFetch, can be used to interact with your app's API.
Install the @gadgetinc/react package:
terminal
yarnadd @gadgetinc/react
Set up the Provider in your extension by wrapping the exported extension component or app with the Provider component and passing in your API client instance:
extensions/your-extension-name/src/Extension.jsx
React
1import{Provider}from"@gadgetinc/react";
2import{ api }from"./api";
3
4exportdefaultreactExtension(TARGET,()=>(
5<Providerapi={api}>
6<App/>
7</Provider>
8));
1import{Provider}from"@gadgetinc/react";
2import{ api }from"./api";
3
4exportdefaultreactExtension(TARGET,()=>(
5<Providerapi={api}>
6<App/>
7</Provider>
8));
Now you can use the @gadgetinc/react hooks to interact with your app's API.
Admin extensions
By default, Shopify's Admin extensions will add an Authentication header to requests made by the extension.
Your Gadget app will automatically handle these incoming requests, and grant them the shopify-app-users role. This means you can use your api client like you would in an embedded admin frontend, with or without the @gadgetinc/react hooks.
Here's an example of a simple Admin extension making an authenticated request to a custom updateDescription action on the shopifyProduct model:
69<NumberFieldlabel="Select a word count"value={wordCount}onChange={setWordCount}/>
70</BlockStack>
71</AdminAction>
72);
73}
Checkout extensions
Checkout extensions are making network requests from an unauthenticated context, the Shopify checkout. This means that requests made to your app's API will be granted the unauthenticated role. Make sure any data passed into the checkout extensions is safe to be seen by any buyer!
Custom apps
For custom apps where you do not need multi-tenancy per shop, you can make requests using the API client:
You can still enforce shop multi-tenancy by passing the Shopify session token with your request.
Sending the session token
When you send Shopify's session token to Gadget, you need to use the ShopifySessionToken prefix in the Authorization header. This is ensures that your Gadget actions have the correct shop context.
Gadget provides a @gadgetinc/shopify-extensions package you can install into your extension that makes it easy to add the session token as a header to all requests made using your Gadget app's API client.
You can install this package in your extension by running this in the extensions/<your-extension-name> folder:
terminal
yarnadd @gadgetinc/shopify-extensions
Then you can make use of the exported Provider and useGadget hook to automatically add the session token to requests made using your API client:
28// use 'ready' to pause hooks until the API client is ready to make authenticated requests
29pause:!ready,
30});
31
32// the rest of your extension component...
33}
If you aren't using your app's API client, this example shows how to send the session token in a fetch request when reading model data using a findOne query:
Post-purchase extensions are a type of checkout extension that requires a JSON Web Token (JWT) to be signed and passed to the extension. This signing can be done in your app backend by passing the JWT from the extension to Gadget as an Authorization: Bearer header.
For example, in your post-purchase extension, you can make a request to get offers and determine if you should render the extension:
extensions/your-extension-name/src/index.jsx
React
1/**
2 * Extend Shopify Checkout with a custom Post Purchase user experience.
3 * This template provides two extension points:
4 *
5 * 1. ShouldRender - Called first, during the checkout process, when the
6 * payment page loads.
7 * 2. Render - If requested by `ShouldRender`, will be rendered after checkout
8 * completes
9 */
10// other imports such as React state hooks and extension components are omitted for brevity
Note that post-purchase extensions require the Shopify Partner app API key and secret to be stored as environment variables in your Gadget app.
Customer account UI extensions
Gadget's support for customer account UI extensions is in beta. See the guide for more information.
Theme app extensions
Theme app extensions are different from other types of extensions because they are built using Liquid and JavaScript. They are not Node projects, so there is no package.json where a Gadget API client can be installed.
Instead, you need to:
Include your app's direct script tag to use the API client in a theme app extension .liquid block:
When you add your script tag, make sure the domain has the correct environment tag!
For example, if you are working in the development environment, your script tag src should look like https://YOUR-GADGET-DOMAIN--development.gadget.app/api/client/web.min.js
Create a file in extensions/your-extension-name/assets, and initialize the API client: