External frontends 

When to use external frontends? 

Gadget has built-in support for developing and hosting frontends using Vite, a modern frontend build tool. Gadget's frontend hosting is reliable, performant, and grouped in with the deploy process for the rest of your application, so we recommend using Gadget's frontend hosting if possible for the best experience. Gadget's frontend hosting has a number of limitations currently however that may merit using a different, purpose-built frontend host.

  • Currently, JS frontends built with Gadget's Vite integration are not rendered server-side and are only rendered client-side. This is generally ok for applications that live behind user-specific authentication, like Shopify apps or other SaaS tools, but not ok for marketing websites, blogs, or other web-page-like content. If you need server-side rendering, you may want to pick a hosting platform with good server-side rendering or static-site generation support.
  • Gadget has great support for serverless functions and business logic via models, actions, and HTTP routes, but Gadget doesn't have support for edge functions. If you need server-side code to run extremely close to your users for maximum performance, other hosting platforms have edge function support. Note: Edge functions on other platforms can still access data from a Gadget backend, and if they need to, the performance benefits of edge functions are largely negated. Gadget recommends edge functions only for stateless workloads that don't need to adjust their responses based on server-side state, or workloads which can use stale data. For applications that need an up-to-date view of backend data, Gadget recommends Gadget's hosting.

Setting up an external frontend for a Gadget application works like any other JavaScript application project -- you create a codebase, install the Gadget API client, and use it to communicate with your Gadget backend. Gadget's full-featured React hooks library @gadgetinc/react is the easiest way to communicate from a frontend back to Gadget, so Gadget recommends using one of the React stacks to build an external frontend.

Using external an React app 

create-react-app is a simple environment for building React applications maintained by the React team. create-react-app works great for structuring a React frontend app that talks to a Gadget backend app over GraphQL, and is easy to deploy on platforms like Vercel or Netlify.

create-react-app apps integrate with Gadget by installing the API Client for your app and the @gadgetinc/react hooks library.

1. Setting up an external React app

If you don't already have a create-react-app application, you can create one:

npx create-react-app example-app-frontend
# or
yarn create react-app example-app-frontend
# then
cd example-app-frontend

Next, you need to install your Gadget backend app's client package.

To install the example-app node package, register the Gadget NPM registry for the @gadget-client package scope:

terminal
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.

With your client package installed, you can instantiate it in any file, but we usually name the file src/api.js:

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

Once you have the required packages, you must complete the @gadgetinc/react setup and provide the API client to the hooks library. For simplicity, we'll add the Provider from @gadgetinc/react to src/index.js like so:

src/index.jsx
React
1// add these imports
2import { Provider } from "@gadgetinc/react";
3import { api } from "./api";
4// existing imports
5import React from "react";
6import ReactDOM from "react-dom/client";
7import "./index.css";
8import App from "./App";
9
10const root = ReactDOM.createRoot(document.getElementById("root"));
11root.render(
12 <React.StrictMode>
13 <Provider api={api}>
14 <App />
15 </Provider>
16 </React.StrictMode>
17);
1// add these imports
2import { Provider } from "@gadgetinc/react";
3import { api } from "./api";
4// existing imports
5import React from "react";
6import ReactDOM from "react-dom/client";
7import "./index.css";
8import App from "./App";
9
10const root = ReactDOM.createRoot(document.getElementById("root"));
11root.render(
12 <React.StrictMode>
13 <Provider api={api}>
14 <App />
15 </Provider>
16 </React.StrictMode>
17);

With the provider in place, you can now use the @gadgetinc/react hooks library in your app. For example, we can fetch a list of Task records from your app's GraphQL API with the useFindMany hook in the App component:

src/App.jsx
React
1import { api } from "./api";
2import "./App.css";
3import { useFindMany } from "@gadgetinc/react";
4
5export default function App() {
6 const [{ data, error, fetching }] = useFindMany(api.task);
7 return (
8 <div className="App">
9 <header className="App-header">
10 {fetching && <p>Loading...</p>}
11 {error && <p>Error: {String(error)}</p>}
12 {data && JSON.stringify(data)}
13 </header>
14 </div>
15 );
16}
1import { api } from "./api";
2import "./App.css";
3import { useFindMany } from "@gadgetinc/react";
4
5export default function App() {
6 const [{ data, error, fetching }] = useFindMany(api.task);
7 return (
8 <div className="App">
9 <header className="App-header">
10 {fetching && <p>Loading...</p>}
11 {error && <p>Error: {String(error)}</p>}
12 {data && JSON.stringify(data)}
13 </header>
14 </div>
15 );
16}

Your create-react-app frontend is set up and ready for use with your Gadget backend!

Using Next.js 

Next.js is a popular framework for building React applications that require minimal setup to get going, and it works great for building frontends for Gadget applications. Next.js has built-in support for server-side rendering and is straightforward to deploy on platforms like Vercel or Netlify.

next.js apps integrate with Gadget by installing the API Client for your app, and the @gadgetinc/react hooks library.

Gadget supports both the next.js App Router and the Pages Router.

Setting up a Next.js app 

If you don't already have a next.js application, you can create one with the create-next-app utility:

plain
npx create-next-app@latest
// or
yarn create next-app
// or
pnpm create next-app

Next, you need to install your Gadget backend app's client package.

To install the example-app node package, register the Gadget NPM registry for the @gadget-client package scope:

terminal
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.

With your client package installed, you can instantiate it in any file, but usually, we name the file api.js:

api.js
JavaScript
1import { Client } from "@gadget-client/example-app";
2export const api = new Client({
3 // for client side data access, we don't pass anything and the client will default to using the browser session authentication mode
4 // for server side data access, pass an API key by uncommenting the line below
5 // authenticationMode: { apiKey: "gsk-some-api-key-here" }
6});
1import { Client } from "@gadget-client/example-app";
2export const api = new Client({
3 // for client side data access, we don't pass anything and the client will default to using the browser session authentication mode
4 // for server side data access, pass an API key by uncommenting the line below
5 // authenticationMode: { apiKey: "gsk-some-api-key-here" }
6});

For users of Next.js' App Router and React Server Components, you can now start making requests to your Gadget backend from the server side within server components.

app/tasks/page.jsx
React
1import { api } from "../../api";
2
3export default async function TasksPage() {
4 const tasks = await api.task.findMany({
5 select: {
6 id: true,
7 title: true,
8 status: true,
9 },
10 sort: { createdAt: "Descending" },
11 first: 10,
12 });
13
14 return (
15 <div>
16 <h1>Tasks</h1>
17 <ul>
18 {tasks.map((task) => (
19 <li key={task.id}>
20 {task.title} - {task.status}
21 </li>
22 ))}
23 </ul>
24 </div>
25 );
26}
1import { api } from "../../api";
2
3export default async function TasksPage() {
4 const tasks = await api.task.findMany({
5 select: {
6 id: true,
7 title: true,
8 status: true,
9 },
10 sort: { createdAt: "Descending" },
11 first: 10,
12 });
13
14 return (
15 <div>
16 <h1>Tasks</h1>
17 <ul>
18 {tasks.map((task) => (
19 <li key={task.id}>
20 {task.title} - {task.status}
21 </li>
22 ))}
23 </ul>
24 </div>
25 );
26}

Setting up client-side data access 

Gadget's API client object can be used to make server-side requests with await like any other API client. But, if you want to allow access to data straight from your app's frontend in the browser, you can also use the client-side React hooks from @gadgetinc/react. See the sections below for more details on server-side vs client-side data access.

To setup @gadgetinc/react for client-side data access, you must install the package and set up the <Provider/> component with an instance of your API client. React providers in next.js are usually added in the _app.js file like so:

pages/_app.jsx
React
1import { Provider } from "@gadgetinc/react";
2import { api } from "../api";
3import type { AppProps } from 'next/app'
4
5export default function MyNextApp({ Component, pageProps }: AppProps) {
6 return (
7 <Provider api={api}>
8 <Component {...pageProps} />
9 </Provider>
10 );
11}
1import { Provider } from "@gadgetinc/react";
2import { api } from "../api";
3import type { AppProps } from 'next/app'
4
5export default function MyNextApp({ Component, pageProps }: AppProps) {
6 return (
7 <Provider api={api}>
8 <Component {...pageProps} />
9 </Provider>
10 );
11}

With the provider in place, you can now use the @gadgetinc/react hooks library in your next.js pages. For example, we can fetch a list of Task records from your app's GraphQL API with the useFindMany hook:

pages/index.jsx
React
1import { useFindMany } from "@gadgetinc/react";
2import { TaskCard } from "../components/TaskCard";
3
4export default function Home() {
5 const [{ data, fetching, error }] = useFindMany(api.task, {
6 sort: { createdAt: "Descending" },
7 first: 30,
8 });
9 return (
10 <div>
11 <h1>Posts</h1>
12 {fetching && <div class="spinner" />}
13 {error && <div status="error">{error.message}</div>}
14 {data && data.map((task) => <TaskCard key={task.id} task={task} />)}
15 </div>
16 );
17}
1import { useFindMany } from "@gadgetinc/react";
2import { TaskCard } from "../components/TaskCard";
3
4export default function Home() {
5 const [{ data, fetching, error }] = useFindMany(api.task, {
6 sort: { createdAt: "Descending" },
7 first: 30,
8 });
9 return (
10 <div>
11 <h1>Posts</h1>
12 {fetching && <div class="spinner" />}
13 {error && <div status="error">{error.message}</div>}
14 {data && data.map((task) => <TaskCard key={task.id} task={task} />)}
15 </div>
16 );
17}

You can find more examples of next.js frontends built using Gadget in the Gadget examples repo.

Server-side vs client-side API access 

Next.js supports two main ways of accessing data:

  • requests made by the node.js process running server-side in a React Server Component, getStaticProps or getServerSideProps function
  • requests made by the browser running client-side in a React hook, like use-fetch, react-query, urql or @gadgetinc/react.

Your Gadget app's API supports both of these methods of data access.

Gadget generally recommends client-side data access for your next.js applications. Client-side data access is fastest for the user, as their browser isn't making extra requests to a server-side process that then makes requests to a Gadget API. Client-side access also allows the API to customize responses for the user to limit data access permissions and to power things like logged in/logged out state. However, server-side data access allows further data processing and is sometimes necessary to power things like next.js' getStaticPaths for static site generation, or to allow access to a limited subset of data.

If you'd like to discuss your use case, we're always happy to help in our Discord.

Client-side data access 

For accessing data on the client, Gadget recommends using the @gadgetinc/react hooks library:

api.js
JavaScript
import { Client } from "@gadget-client/example-app";
// instantiate a client with the auth powered by each user's browser (the default)
export const api = new Client();
import { Client } from "@gadget-client/example-app";
// instantiate a client with the auth powered by each user's browser (the default)
export const api = new Client();

Then, after wrapping our app in the <Provider/> from @gadgetinc/react, we can use React hooks to access data:

pages/index.jsx
React
1import { api } from "../api";
2import { useFindMany } from "@gadgetinc/react";
3import { TaskCard } from "../components/TaskCard";
4
5export default function Home() {
6 // use a React hook for data access within the component
7 const [{ data, fetching, error }] = useFindMany(api.task);
8 // ...
9 return (
10 <div>
11 {data.map((task) => (
12 <TaskCard key={task.id} task={task} />
13 ))}
14 </div>
15 );
16}
1import { api } from "../api";
2import { useFindMany } from "@gadgetinc/react";
3import { TaskCard } from "../components/TaskCard";
4
5export default function Home() {
6 // use a React hook for data access within the component
7 const [{ data, fetching, error }] = useFindMany(api.task);
8 // ...
9 return (
10 <div>
11 {data.map((task) => (
12 <TaskCard key={task.id} task={task} />
13 ))}
14 </div>
15 );
16}

Server-side data access 

For accessing data on the server, Gadget recommends using the standard, imperative style API client object without hooks from @gadgetinc/react. It's easiest to instantiate your Gadget API client with an API key for authentication and then use it when generating server-side props.

api.js
JavaScript
import { Client } from "@gadget-client/example-app";
// instantiate a client with an API key that will only be used server-side
export const api = new Client({
authenticationMode: { apiKey: "gsk-some-api-key-here" },
});
import { Client } from "@gadget-client/example-app";
// instantiate a client with an API key that will only be used server-side
export const api = new Client({
authenticationMode: { apiKey: "gsk-some-api-key-here" },
});

Then in your next.js pages, we can make API calls using the api.model.findMany style finders and actions.

For users of the Next.js App router, you can make these calls in your React Server Components:

app/tasks/page.jsx
React
1import { api } from "../../api";
2
3export default async function TasksPage() {
4 const tasks = await api.task.findMany({ first: 10 });
5 return (
6 <div>
7 {tasks.map((task) => (
8 <TaskCard key={task.id} task={task} />
9 ))}
10 </div>
11 );
12}
1import { api } from "../../api";
2
3export default async function TasksPage() {
4 const tasks = await api.task.findMany({ first: 10 });
5 return (
6 <div>
7 {tasks.map((task) => (
8 <TaskCard key={task.id} task={task} />
9 ))}
10 </div>
11 );
12}

For users of the Next.js Pages router, you can make these calls in your getServerSideProps or getStaticProps functions:

pages/index.jsx
React
1import { api } from "../api";
2import { TaskCard } from "../components/TaskCard";
3import type { InferGetServerSidePropsType, GetServerSideProps } from "next";
4
5export const getServerSideProps: GetServerSideProps = async () => {
6 return {
7 props: {
8 tasks: await api.task.findMany({ first: 10 }),
9 },
10 };
11};
12
13export default function Home(props: InferGetServerSidePropsType<typeof getServerSideProps>) {
14 // use props passed from the getServerSideProps function
15 return (
16 <div>
17 {props.tasks.map((task) => (
18 <TaskCard key={task.id} task={task} />
19 ))}
20 </div>
21 );
22}
1import { api } from "../api";
2import { TaskCard } from "../components/TaskCard";
3import type { InferGetServerSidePropsType, GetServerSideProps } from "next";
4
5export const getServerSideProps: GetServerSideProps = async () => {
6 return {
7 props: {
8 tasks: await api.task.findMany({ first: 10 }),
9 },
10 };
11};
12
13export default function Home(props: InferGetServerSidePropsType<typeof getServerSideProps>) {
14 // use props passed from the getServerSideProps function
15 return (
16 <div>
17 {props.tasks.map((task) => (
18 <TaskCard key={task.id} task={task} />
19 ))}
20 </div>
21 );
22}

Using a Gadget API key to initialize your Gadget client on the server gives you full read or write access to the database. If working on a Shopify app, shop tenancy is not enforced.

This key should never be exposed in a browser, and users should never be able to see it. This should only be used when connecting to another secure server.

Alternative frameworks 

Gadget currently doesn't have specific adapter libraries built for client-side frameworks other than React, like Vue, Svelte, or Solid. However, your Gadget application has a rich GraphQL API ready to read and write data, which means you can use any existing GraphQL client for these frameworks to interface with your Gadget app right out of the box.

urql for Vue and Svelte 

Gadget recommends urql as a high-quality GraphQL client with built-in support for Vue and Svelte. Your generated JS API Client package uses urql under the hood, so it's easy to re-use the existing API authentication and connectivity code from the Gadget API client within a Vue or Svelte app.

For example, we can wire up a generated api Client instance for use within a Vue app by accessing the api.connection.currentClient urql.Client instance for use in Vue:

JavaScript
1// import the urql vue bindings
2import { provideClient } from "@urql/vue";
3// import and instantiate your Gadget API client, which uses urql under the hood
4import { Client } from "${props.currentApp.jsPackageIdentifier}";
5// create an instance of the Gadget API Client
6// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info
7export const api = new Client();
8// provide the pre-configured urql client instance from the Gadget API Client to the vue bindings
9provideClient(api.connection.currentClient);
1// import the urql vue bindings
2import { provideClient } from "@urql/vue";
3// import and instantiate your Gadget API client, which uses urql under the hood
4import { Client } from "${props.currentApp.jsPackageIdentifier}";
5// create an instance of the Gadget API Client
6// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info
7export const api = new Client();
8// provide the pre-configured urql client instance from the Gadget API Client to the vue bindings
9provideClient(api.connection.currentClient);

Read more about @urql/vue in urql's docs.

For Svelte support, you can also use the generated Gadget API Client to set up the @urql/svelte bindings:

JavaScript
1// import the urql svelte bindings
2import { setContextClient } from "@urql/svelte";
3// import and instantiate your Gadget API client, which uses urql under the hood
4import { Client } from "${props.currentApp.jsPackageIdentifier}";
5// create an instance of the Gadget API Client
6// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info
7export const api = new Client();
8// provide the pre-configured urql client instance from the Gadget API Client to the svelte context bindings
9setContextClient(api.connection.currentClient);
1// import the urql svelte bindings
2import { setContextClient } from "@urql/svelte";
3// import and instantiate your Gadget API client, which uses urql under the hood
4import { Client } from "${props.currentApp.jsPackageIdentifier}";
5// create an instance of the Gadget API Client
6// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info
7export const api = new Client();
8// provide the pre-configured urql client instance from the Gadget API Client to the svelte context bindings
9setContextClient(api.connection.currentClient);

Read more about @urql/svelte in urql's docs.

Alternative libraries 

Because your Gadget app's GraphQL API is spec compliant, any GraphQL client can be used to make requests to it. You can use libraries like graphql-request in JS, apollo-client for JS, Swift, and Kotlin, or even any HTTP request library that can send and receive JSON.

For example, we can make an API call to Gadget using the cURL command line utility:

terminal
curl -X POST \
-H "Content-Type: application/json" \
-d '{ "query": "query { gadgetMeta { name } }" }' \
https://example-app.gadget.app/api/graphql

Deploying and hosting external frontends 

Frontends built using React or other client-side frameworks need to be deployed to a hosting provider. Gadget recommends using Vercel or Netlify for hosting your frontends, as both make it easy to deploy and work great with existing Gadget tooling.

Vercel 

You will need a Vercel account.

Need to connect your existing Shopify CLI 3.X app to Gadget? Check out our Shopify CLI Connection tutorial.

For more CLI-specific docs, scroll down to the Shopify CLI 3.X section.

  1. Setting up your Vercel project

First, we need to ensure that Vercel will be able to install your @gadget-client package by adding the Gadget NPM registry. If you haven't already, ensure that you have a .npmrc file your repo next to the package.json referring to the @gadget-client repo. For example, if your frontend resides in the web/frontend folder of your repo, create the file web/frontend/.npmrc with these contents:

.npmrc next to your frontend package.json
@gadget-client:registry=https://registry.gadget.dev/npm

Next, we can log in to Vercel and create a new project. To do so, click on the Add New... dropdown on the right side and select "Project".

Vercel create new project image

From here we can click on "Continue with GitHub" and search for the repository we want to deploy from.

Vercel import repository image

Clicking Import will bring you to the next page where we can configure the project. From here you can apply build and output settings as well as environment variables.

To ensure Vercel connects to the Production environment of your Gadget application set the NODE_ENV environment variable to production in the Vercel configuration:

Vercel configure NODE_ENV environment variable

If you need to modify the build settings or environment variables in the future, you can do so on the Settings page.

Embedded Shopify apps

To deploy Shopify applications using external frontends to Vercel, some more set-up is required.

First, open the Environment Variables dropdown and add your SHOPIFY_API_KEY and value for your Production Shopify application. You can find the value for SHOPIFY_API_KEY either on the "Overview" or "Client credentials" pages of your Shopify Partners Dashboard under the Client ID header.

Vercel environment variables section image

For your Vercel frontend to access your Gadget backend, you must also ensure the App URL value on the Shopify Partner Dashboard and the Gadget Connections page reflect your new Vercel application's URL. In the Vercel dashboard for your deployed project, copy the URL beside the site preview, under the header "DOMAINS".

Vercel domain location image

In Gadget navigate to your active Shopify connection. Click on the pencil icon to the right of the Shopify Apps section to edit your connection, and replace the App URL with your deployed application's domain.

Section in Gadget that displays Shopify app connections

On the Partners Dashboard in Shopify, navigate to the Configuration page, then to the URLs section. Replace the current App URL with your deployed application's domain and click save to confirm your changes.

Shopify app URLs section in Configuration
  • Next.js

If you're using next.js to build your external frontend, you need to take one extra step if you intend to deploy to the Shopify App Store. Shopify requires that each Shopify App returns security headers when rendering the application for the iframe Shopify embeds it in. To ensure your next.js app replies with the correct Content-Security-Policy header, add a middleware.js file to your next.js application root:

/middleware.js
JavaScript
1import { NextResponse } from "next/server";
2import type { NextRequest } from "next/server";
3
4export function middleware(request: NextRequest) {
5 const shop = request.nextUrl.searchParams.get("shop");
6 const response = NextResponse.next();
7 response.headers.set(
8 "content-security-policy",
9 `frame-ancestors https://${shop}/ https://admin.shopify.com;`
10 );
11 return response;
12}
1import { NextResponse } from "next/server";
2import type { NextRequest } from "next/server";
3
4export function middleware(request: NextRequest) {
5 const shop = request.nextUrl.searchParams.get("shop");
6 const response = NextResponse.next();
7 response.headers.set(
8 "content-security-policy",
9 `frame-ancestors https://${shop}/ https://admin.shopify.com;`
10 );
11 return response;
12}

Read more about Shopify's iframe security requirements in Shopify's docs.

  • Shopify CLI 3.X If you're using the Shopify CLI to build and deploy a Shopify application, you need to take some extra steps for it to deploy correctly to Vercel. You can follow the Shopify iFrame protection example to test that you are setting the CSP header correctly.
    Ensure you have completed all the steps to set up your Shopify CLI application for Gadget backends in the Shopify CLI Connection tutorial before deploying to Vercel
    First, we need to tell Vercel what command to run to build this app. We need to add the vercel-build script within web/package.json:
web/package.json
json
{
"scripts": {
"vercel-build": "cd frontend && npm install && npm run build"
}
}

This script instructs Vercel to install dependencies for both the node.js file server application and frontend Vite application, and then build the production assets for the frontend. Second, add a new file to the web directory of your Shopify app called vercel.json with the following contents.

web/vercel.json
json
1{
2 "version": 2,
3 "outputDirectory": ".",
4 "builds": [
5 {
6 "src": "index.js",
7 "use": "@vercel/node",
8 "config": {
9 "includeFiles": ["./frontend/dist/**"]
10 }
11 }
12 ],
13 "routes": [{ "src": "/(.*)", "dest": "/" }]
14}

This file instructs Vercel to:

  • copy the built production assets for the Vite app into the serverless function for serving the application
  • run the index.js file as a node.js application to serve all the static files with correct headers In the Configure Project section, edit the Root Directory option to read web/. This will direct Vercel to build and deploy the frontend for your Shopify app, and ignore any extensions or other files in your repo. Vercel output directory section image Once this is complete, commit your changes to your repo and push to Vercel. Watch the logs to make sure that the deployment was successful. Once deployment is complete, you should see a congratulations page with a preview of your live site to the left. Vercel deployment success page image Congrats, your app should now be deployed on Vercel! You can now install your app on a store or view your app in a store admin on which it has previously been installed.

Netlify 

You will need an account with Netlify.

Need to connect your existing Shopify CLI 3.X app to Gadget? Check out our Shopify CLI Connection tutorial. For more CLI-specific docs, scroll down to the Shopify CLI 3.X section.

Setting up your Netlify Site 

First, we need to make sure that Netlify's build process can find the @gadget-client package for your application.

If you haven't already, ensure that you have a .npmrc file in your repo next to the package.json that manages the @gadget-client dependency. For example, if your frontend resides in the web/frontend folder of your repo, create the file web/frontend/.npmrc with these contents:

.npmrc next to your frontend package.json
@gadget-client:registry=https://registry.gadget.dev/npm

This configuration ensures that npm install or yarn install will correctly fetch your Gadget API client package from the Gadget registry. Next, we need to log in to Netlify and add a new site. To do so, click on the "Add new site" dropdown on the right side of the sites section and select "Import an existing project".

Netlify add a new site image

From here we can click on "GitHub" and search for the repository we want to deploy from.

Netlify repository import page image

Clicking on the repository name will bring you to the next page where we can configure the project. From here you can modify Build settings and add environment variables to your site.

Embedded Shopify Apps

To deploy Shopify applications using external frontends to Netlify, some more set-up is required.

Add the environment variable SHOPIFY_API_KEY by clicking on Show advanced and New variable. You can find the value for SHOPIFY_API_KEY either on the "Overview" or "Client credentials" pages of your Shopify Partners Dashboard under the Client ID header.

Netlify environment variables section image

If you forget to add environment variables or need to modify the build settings, you can do so again on the "Site settings" page later on.

Once this is complete you can click on "Deploy site" and watch the logs, by clicking on the yellow 'Site deploy in progress' link, to make sure that the deployment was successful.

Netlify build logs link image

Once deployment is successful, you should see the text "Site is live" at the bottom of the build logs and a site preview on the "Site overview" page next to your site's domain. For your application to be accessed by users, you must first change the app URL on the Shopify Partner Dashboard and the Gadget Connections page. In the Netlify "Site overview" page for your deployed site, copy the URL beside the site preview.

Netlify domain location image

In Gadget navigate to your active Shopify connection. From here we can click on the pencil icon to the right of the Shopify Apps section. Replace the App URL with your deployed application's domain.

Section in Gadget that displays Shopify app connections

On the Partners Dashboard in Shopify, navigate to the Configuration page, then to the URLs section. From here replace the current URL with your deployed application's domain and click save to confirm your changes.

Shopify app URLs section in Configuration

Shopify CLI 3.X

Before submitting your app to Shopify for review, you will need to add a couple of files to conform with Shopify's Content Security Policy (CSP) header requirements. You can follow the Shopify iframe protection example to test that you are setting the CSP header correctly.

Ensure you have completed all the steps to set up your Shopify CLI application for Gadget backends in the Shopify CLI Connection tutorial before deploying to Vercel

You need to create a Netlify Edge function. Edge functions are serverless functions that are hosted on AWS Lambda. Create a new folder at the root of your project, netlify/edge-functions, and add a file named csp.js. In this file, add the following code snippet:

netlify/edge-functions/csp.js
JavaScript
1import type { Context } from "@netlify/edge-functions";
2
3export default async function (request: Request, context: Context) {
4 const url = new URL(request.url);
5 const shop = url.searchParams.get("shop");
6
7 if (shop) {
8 const response = await context.next();
9 response.headers.set(
10 "Content-Security-Policy",
11 `frame-ancestors https://${shop} https://admin.shopify.com;`
12 );
13 return response;
14 }
15}
1import type { Context } from "@netlify/edge-functions";
2
3export default async function (request: Request, context: Context) {
4 const url = new URL(request.url);
5 const shop = url.searchParams.get("shop");
6
7 if (shop) {
8 const response = await context.next();
9 response.headers.set(
10 "Content-Security-Policy",
11 `frame-ancestors https://${shop} https://admin.shopify.com;`
12 );
13 return response;
14 }
15}

To make sure that this Netlify Edge function deploys, you will need to add a netlify.toml at the root directory with the following configurations:

netlify.toml
toml
[[edge_functions]]
path = "/*"
function = "csp"

You also need to navigate to the build settings and change the Publish directory input field to web/frontend/dist.

Netlify publish directory section image

Once a new deployment is triggered, you're all finished.

Congrats, your app should now be deployed on Netlify! You can now install your app on a store or view your app in a store admin on which it has previously been installed.

Heroku 

You will need an account with Heroku. We also recommend using GitHub to host your Git repository.

Are you trying to connect your existing Shopify CLI 3.X app to Gadget? Check out our Shopify CLI Connection tutorial.

For more CLI-specific docs, scroll down to the Shopify CLI 3.X section.

  1. Setting up your Heroku App

First, we need to make sure that Heroku's node buildpacks can find the @gadget-client package for your application. If you haven't already, ensure that you have a .npmrc file your repo next to the package.json referring to the @gadget-client repo. For example, if your frontend resides in the web/frontend folder of your repo, create the file web/frontend/.npmrc with these contents:

.npmrc next to your frontend package.json
@gadget-client:registry=https://registry.gadget.dev/npm

This configuration ensures that npm install or yarn install will correctly fetch your Gadget API client package from the Gadget registry.

Next, we must log in to Heroku and create an app. To do so, click on the "New" dropdown on the right side and select "Create new app".

Heroku create new app button

From here you can name your app as desired and select the region in which to deploy your app.

Heroku app naming page

Once the app is created you can add environment variables by going to the Settings page and scrolling down to the Config Vars section and clicking "Reveal Config Vars".

Embedded Shopify Apps

To deploy Shopify applications using external frontends to Heroku, some more set-up is required.

Add the SHOPIFY_API_KEY to your Heroku app's environment variables. The SHOPIFY_API_KEY can be found in your Shopify Partners Dashboard on the "Overview" or "Client credentials" pages under the header Client ID. The environment variable should look something like the below image.

Example of the SHOPIFY API KEY environment variable

To deploy the application navigate to the "Deploy" tab. Click on the GitHub deployment method. From here you can select the repository you wish to deploy from. Type in the name of your repository and click enter, then connect that repository with Heroku.

Heroku repository selection section

Now that the repository is connected to Heroku, scroll down to the Manual deploy section, choose the branch you want to deploy from and click on "Deploy Branch".

Heroku manual deploy section

To connect your frontend with your Gadget backend, you must first change the app URL on the Shopify Partner Dashboard and the Gadget Connections page. Copy the URL of the deployed app on Heroku under the "Settings" tab and Domains section.

Heroku domains section

In Gadget navigate to your active Shopify connection. From here we can click on the pencil icon to the right of the Shopify Apps section. Replace the App URL with your deployed application's domain.

Section in Gadget that displays Shopify app connections

On the Partners Dashboard in Shopify, navigate to the Configuration page, then to the URLs section. From here replace the current URL with your deployed application's domain and click save to confirm your changes.

Shopify app URLs section in Configuration

Shopify CLI 3.X

Before deploying your site you will need to add a couple of files to conform with the Shopify Content Security Policy (CSP) header requirements. You can follow the Shopify iFrame protection example to test that you are setting the CSP header correctly.

You will need the Heroku CLI and Docker installed on your machine to follow these steps.

Ensure you have completed all the steps to set up your Shopify CLI application for Gadget backends in the Shopify CLI Connection tutorial before deploying to Vercel

Log in to Heroku using the CLI:

zsh
heroku login

Once the login process is complete, you will need to log in to the Heroku container registry from the root folder of your application:

zsh
heroku container:login

You now need to set your existing Heroku app to act as a docker container. Make sure to modify the app name to match Heroku:

zsh
heroku apps:stacks:set -a <heroku-app-name> container

Now that the Heroku deployment configurations are set up, you will need to add a heroku.yml file at the root of your application. Replace <SHOPIFY_API_KEY> with your Shopify API key:

heroku.yml
yml
build:
docker:
web: Dockerfile
config:
SHOPIFY_API_KEY: <SHOPIFY_API_KEY>

Once all these prior steps have been completed and you have committed your changes to the linked repository, you are ready to deploy!

Congrats, your app should now be deployed on Heroku! You can now install your app on a store or view your app in a store admin on which it has previously been installed.

Was this page helpful?