Building frontends
Gadget applications can host user interfaces for the people using your application. Using HTTP Routes, you can render HTML pages or other static files which can be accessed by anyone with a web browser. Frontends hosted on Gadget can use data from Gadget models to serve dynamic content and can be extended using plugins from the fastify
.
Picking a frontend technology stack
There are two main ways to build frontends for the modern web:
- server-rendered HTML with light JavaScript enhancement
- client-rendered HTML with JavaScript frameworks and optional server-side rendering
Gadget applications currently support rendering HTML server-side. If you want to use React, Vue, or other client-side oriented frameworks, see the Single page JS Apps section below.
Follow our deployment documentation for more information.
Server-side HTML
Gadget apps are built on Fastify, which has a whole ecosystem of plugins for rendering HTML server-side to send back to the browser. See the Fastify ecosystem page for a list of plugins.
Gadget is currently building an easy-to-use views system for rendering HTML server-side. If you're interested, join us on Discord, and let's discuss!
Single-page JS apps
Building rich or complicated frontend experiences often merits using a frontend JavaScript framework like React or Vue. Gadget doesn't have support for hosting client-side applications built right in. Instead, Gadget recommends using your app's GraphQL API within one of the great toolchains for building and deploying client-side apps out there today.
For more information on your app's GraphQL API, see the API Reference.
React apps
Gadget has a library for making API calls to your app from a React application quickly and easily called @gadgetinc/react
. It provides React hooks like useFindOne
, useFindMany
, and useAction
that fetch data and run actions. @gadgetinc/react
uses the same API client object you might use elsewhere under the hood and calls the same GraphQL API for your app as everything else.
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# oryarn create react-app example-app-frontend# thencd 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 usually we name the file src/api.js
:
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:
1// add these imports2import { Provider } from "@gadgetinc/react";3import { api } from "./api";45// existing imports6import React from "react";7import ReactDOM from "react-dom/client";8import "./index.css";9import App from "./App";1011const 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:
1import { api } from "./api";2import "./App.css";3import { useFindMany } from "@gadgetinc/react";45function App() {6 const [{ data, error, fetching }] = useFindMany(api.task);7 a;89 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// oryarn create next-app// orpnpm 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
:
1import { Client } from "@gadget-client/example-app";23export 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 mode5 // for server side data access, pass an API key by uncommenting the line below6 // 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:
1import { Provider } from "@gadgetinc/react";2import { api } from "../api";34function MyNextApp({ Component, pageProps }: AppProps) {5 return (6 <Provider value={api.connection.currentClient}>7 <Component {...pageProps} />8 </Provider>9 );10}1112export 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:
1import { useFindMany } from "@gadgetinc/react";2import { TaskCard } from "../components/TaskCard";34const Home: NextPage = () => {5 const [{ data, fetching, error }] = useFindMany(api.task, {6 sort: { createdAt: "Descending" },7 first: 30,8 });910 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};1920export 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
orgetServerSideProps
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. In general, 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.
Gadget generally recommends client-side data access for your next.js applications.
Client-side data access
For accessing data on the client, Gadget recommends using the @gadgetinc/react
hooks library:
import { Client } from "@gadget-client/example-app";// instantiate a client with the auth powered by each user's individual 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:
1import { api } from "../api";2import { useFindMany } from "@gadgetinc/react";3import { TaskCard } from "../components/TaskCard";45const Home: NextPage = () => {6 // use a React hook for data access within the component7 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.
1import { Client } from "@gadget-client/@gadget-client/example-app";23// instantiate a client with an API key that will only be used server-side4export 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:
1import { api } from "../api";2import { TaskCard } from "../components/TaskCard";34const Home: NextPage = (props) => {5 // use props passed from the getServerSideProps function6 return (7 <div>8 {props.tasks.map((task) => (9 <TaskCard key={task.id} task={task} />10 ))}11 </div>12 );13};1415export 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 must 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 deployment documentation.
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:
registry=https://registry.npmjs.org/@gadget-client:registry=https://registry.gadget.dev/npm
Shopify Embedded Apps
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.
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:
1// import the urql vue bindings2import { provideClient } from "@urql/vue";3// import and instantiate your Gadget API client, which uses urql under the hood4import { Client } from "@gadget-client/example-app";56// create an instance of the Gadget API Client7// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info8export const api = new Client();910// provide the pre-configured urql client instance from the Gadget API Client to the vue bindings11provideClient(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:
1// import the urql svelte bindings2import { setContextClient } from "@urql/svelte";3// import and instantiate your Gadget API client, which uses urql under the hood4import { Client } from "@gadget-client/example-app";56// create an instance of the Gadget API Client7// Note: requests will be unauthenticated - see https://docs.gadget.dev/guides/access-control for more info8export const api = new Client();910// provide the pre-configured urql client instance from the Gadget API Client to the svelte context bindings11setContextClient(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:
curl -X POST \-H "Content-Type: application/json" \-d '{ "query": "query { gadgetMeta { name } }" }' \https://example-app.gadget.app/api/graphql
Related topics
- Frontend deployment documentation