External frontends 

Gadget applications can serve as a backend for a frontend hosted somewhere else. Your Gadget application's GraphQL API is accessible from anywhere, so you can build out an interface elsewhere and connect back to Gadget easily.

When to go external 

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.

Building an external frontend 

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

Setting up a create-react-app 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:

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 },
});

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

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

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

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
// 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:

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";
2
3export const api = new Client({
4 // for client side data access, we don't pass anything and the client will default to using the browser session authentication mode
5 // for server side data access, pass an API key by uncommenting the line below
6 // authenticationMode: { apiKey: "gsk-some-api-key-here" }
7});

Once you have the required packages, you must complete the @gadgetinc/react set-up to provide the API client to the hooks library. React providers in next.js are usually added in the _app.js file like so:

pages/_app.js
JavaScript
1import { Provider } from "@gadgetinc/react";
2import { api } from "../api";
3
4function MyNextApp({ Component, pageProps }: AppProps) {
5 return (
6 <Provider value={api.connection.currentClient}>
7 <Component {...pageProps} />
8 </Provider>
9 );
10}
11
12export default MyNextApp;

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

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 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 app's GraphQL 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.

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();

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

pages/index.jsx
JavaScript
1import { api } from "../api";
2import { useFindMany } from "@gadgetinc/react";
3import { TaskCard } from "../components/TaskCard";
4
5const Home: NextPage = () => {
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
1import { Client } from "@gadget-client/@gadget-client/example-app";
2
3// instantiate a client with an API key that will only be used server-side
4export const api = new Client({
5 authenticationMode: { apiKey: "gsk-some-api-key-here" },
6});

Then in our next.js pages, we can make API calls using the api.model.findMany style finders built into the api client object, and send the results as props to the client-side component:

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

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.

Hosting 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. You can find more information on how to deploy a frontend application by viewing our external frontend deployment guide.

You must register the Gadget npm registry in all environments where you want to use this package, which would include production systems, continuous integration environments, or build steps like Vercel or Netlify's static site builders. This can be done using the above npm config command, or by writing out an .npmrc file in those environments that points to Gadget for the @gadget-client scope.

To indicate to hosting platforms where to find the @gadget-client/example-app package, create an .npmrc file in the web/frontend directory with the following content:

web/frontend/.npmrc
registry=https://registry.npmjs.org/
@gadget-client:registry=https://registry.gadget.dev/npm

Shopify embedded apps in external frontends 

If you're building a Shopify application, Shopify recommends you build it in React using their Polaris component library and the App Bridge for communicating with the Shopify platform. Gadget has an easy-to-use library for building React frontends for Shopify with Polaris. Read more in the Shopify App Frontend guide.

Gadget also has an example Shopify Embedded App built with next.js you can use as a start in the examples repo.

Installation 

To build a Shopify App frontend with your Gadget backend, you must first set up a React application. Gadget recommends using Gadget's built-in hosting for Shopify frontends if possible, but if not, create-react-app, next.js, or the Shopify CLI's Vite template frontend app work well.

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!

These two packages manage working with the Shopify App Bridge, and provide React hooks for accessing data from your Gadget models.

Next, you need to set up your Gadget api client object and the wrapping Provider React context from @gadgetinc/react-shopify-app-bridge. 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.

src/api.js
JavaScript
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:

src/app.jsx
JavaScript
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";
8
9export function MyApp() {
10 return (
11 <GadgetProvider
12 type={AppType.Embedded}
13 shopifyApiKey="---your Shopify app's API Key (Client ID) from the Partners dashboard---"
14 api={api}
15 >
16 <ProductManager />
17 </GadgetProvider>
18 );
19}

Once the Provider is wrapping your application, you can start to build out components in your embedded application.

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;

Other 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 "@gadget-client/example-app";
5
6// create an instance of the Gadget API Client
7// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info
8export const api = new Client();
9
10// provide the pre-configured urql client instance from the Gadget API Client to the vue bindings
11provideClient(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 "@gadget-client/example-app";
5
6// create an instance of the Gadget API Client
7// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info
8export const api = new Client();
9
10// provide the pre-configured urql client instance from the Gadget API Client to the svelte context bindings
11setContextClient(api.connection.currentClient);

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

Other 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:

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