# Templates  The Gadget platform comes with application templates for our three main use cases: web apps, Shopify apps, and BigCommerce apps. Each template is designed to provide a starting point for your project, with the necessary configuration and boilerplate code to get you up and running quickly. ## Base templates  Base templates are the very basic starting point for your project. They have boilerplate already setup for you, using what the Gadget team regards as best practices. They are designed to be a blank canvas for you to build upon. The available base templates are: | Template | Tenancy | Framework | SSR/SPA | | --- | --- | --- | --- | | Shopify | Public | React | SPA | | Shopify | Custom | React | SPA | | Shopify | Public | Remix | SSR | | Shopify | Custom | Remix | SSR | | BigCommerce | Public | React | SPA | | Web app | Multi-party auth | React Router v7 | SSR | | Web app | Single-party auth | React Router v7 | SSR | | Web app | No auth | React Router v7 | SSR | SSR stands for server-side rendering, and SPA stands for single-page application. ## Expanded templates  Apart from the base templates, we also have expanded templates. These templates are built on top of the base templates and include additional features and configurations. They are designed to provide a more complete starting point for your project. You can find the expanded templates by following [this link](https://gadget.dev/templates) or on [Github](https://github.com/gadget-inc/templates). You can also find the expanded templates from the app selection page. In the left nav, click on templates, and you will see the expanded templates. ## React Router v7  ### Switching between SSR and SPA  This is only available when using the React Router v7 framework mode. You can switch between server-side rendering (SSR) and single-page application (SPA) mode by changing the React Router configs, adding the `ssr` flag set to `false` in the `react-router.config` file. ```typescript import type { Config } from "@react-router/dev/config"; import { reactRouterConfigOptions } from "gadget-server/react-router"; export default { ...reactRouterConfigOptions, ssr: false } satisfies Config; ``` For more information, take a look at the [React Router v7 documentation](https://reactrouter.com/start/framework/rendering). From here, you will need to change the way that data loads, in your routes, from the SSR `loader` function to the `clientLoader` function. ```tsx export const loader = async ({ context, request }: Route.LoaderArgs) => { const url = new URL(request.url); const code = url.searchParams.get("code"); try { await context.api.user.verifyEmail({ code }); return { success: true, error: null }; } catch (error) { return { error: { message: (error as Error).message }, success: false, }; } }; ``` The `clientLoader` can be used in both SSR and SPA. For more information, take a look at the [React Router docs](https://reactrouter.com/start/framework/route-module#clientloader). ```tsx export async function clientLoader({ serverLoader }: Route.ClientLoaderArgs) { // call the server loader (if using SSR) const serverData = await serverLoader(); // And/or fetch data on the client const data = getDataFromClient(); // Return the data to expose through useLoaderData() return data; } ``` Make sure that you also switch any usage of the `gadgetConfig`, loaded in the loader when using SSR, to window.gadgetConfig when using SPA mode. You can do that by first adding the following script tag to the head tag in your `root.tsx` file: ```tsx export const Layout = ({ children }: { children: React.ReactNode }) => { return ( {/* This script tag injects the gadgetConfig object in the window */} Loading...}>{children} ); }; ``` Then switch any usage of the `gadgetConfig` to `window.gadgetConfig` in your components. ### Switching between framework and declarative mode  To switch your React Router v7 mode to declarative mode, you need to add an `index.html` file to the root of your project, including the following code: ```html New Gadget App Welcome Page
``` You'll need to add a `main.tsx` file to the web folder and add the following code: ```tsx import React from "react"; import ReactDOM from "react-dom/client"; import App from "./components/App"; const root = document.getElementById("root"); if (!root) throw new Error("#root element not found for booting react app"); ReactDOM.createRoot(root).render( ); ``` You will then need to add the `App.tsx` file that includes a browser router: ```tsx import { Provider } from "@gadgetinc/react"; import { Suspense, useEffect } from "react"; import { BrowserRouter, Outlet, Route, Routes, useNavigate } from "react-router"; import { api } from "../api"; import "../app.css"; import ForgotPasswordPage from "../routes/forgot-password"; import IndexPage from "../routes/index"; import ProfilePage from "../routes/profile"; import InvitePage from "../routes/invite"; import TeamPage from "../routes/team"; import ResetPasswordPage from "../routes/reset-password"; import SignUpPage from "../routes/sign-up"; import SignedInPage from "../routes/signed-in"; import VerifyEmailPage from "../routes/verify-email"; import AnonLayout from "./layouts/AnonLayout"; import UserLayout from "./layouts/UserLayout"; const App = () => { useEffect(() => { document.title = `${window.gadgetConfig.env.GADGET_APP}`; }, []); return ( }> }> {/* Example routes */} }> } /> } /> } /> } /> } /> }> } /> } /> } /> } /> ); }; const Layout = () => { const navigate = useNavigate(); return ( ); }; export default App; ``` Note that the routes in the above file are an example and your own routes and components may be different. ## Remix  ### Switching from SSR to SPA  You can manually migrate a Remix frontend from SSR mode to SPA mode by following these steps: 1. Add the `ssr: false` flag to the `remix` plugin in the `vite.config.mjs` file: ```typescript import { defineConfig } from "vite"; import { gadget } from "gadget-server/vite"; import { remixViteOptions } from "gadget-server/remix"; import { vitePlugin as remix } from "@remix-run/dev"; export default defineConfig({ plugins: [ gadget(), remix({ ...remixViteOptions, // add this ssr: false, }), ], }); ``` 2. Add the [`/* --GADGET_CONFIG-- */` script tag](https://docs.gadget-canary.xyz/guides/frontend/remix-in-gadget#dynamic-configuration-injection-in-spa-mode) to `web/root.jsx`. 3. SPA mode does not support the `loader` and `action` functions. You need to use Gadget's React hooks and autocomponents to read and write data in your frontend. ## Switching from Remix to React Router v7  To switch from Remix to React Router v7, you need to add some packages and files to your application. Add the following packages as dependencies: ```yarn yarn add react-dom react-router @react-router/fs-routes ``` Add the following packages as devDependencies: ```yarn yarn add -D @react-router/dev ``` Remove the following packages from your dependencies: ```yarn yarn remove @remix-run/node @remix-run/react @remix-run/dev ``` Change your `root.tsx` to the following: ```tsx import { Provider as GadgetProvider } from "@gadgetinc/react"; import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; import { Suspense } from "react"; import { api } from "./api"; import "./app.css"; import type { GadgetConfig } from "gadget-server"; import type { Route } from "./+types/root"; import { ErrorBoundary as DefaultGadgetErrorBoundary } from "gadget-server/react-router"; export const links = () => [{ rel: "stylesheet", href: "https://assets.gadget.dev/assets/reset.min.css" }]; export const meta = () => [ { charset: "utf-8" }, { name: "viewport", content: "width=device-width, initial-scale=1" }, { title: "Gadget React Router app" }, ]; export type RootOutletContext = { gadgetConfig: GadgetConfig; csrfToken: string; }; export const loader = async ({ context }: Route.LoaderArgs) => { const { session, gadgetConfig } = context; return { gadgetConfig, csrfToken: session?.get("csrfToken"), }; }; export default function App({ loaderData }: Route.ComponentProps) { const { gadgetConfig, csrfToken } = loaderData; return ( ); } // Default Gadget error boundary component // This can be replaced with your own custom error boundary implementation // For more info, checkout https://reactrouter.com/how-to/error-boundary#1-add-a-root-error-boundary export const ErrorBoundary = DefaultGadgetErrorBoundary; ``` Change the `vite.config` file to the following: ```typescript import path from "path"; import { reactRouter } from "@react-router/dev/vite"; import { gadget } from "gadget-server/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [gadget(), reactRouter()], resolve: { alias: { "@": path.resolve(__dirname, "./web"), }, }, }); ``` Add the following files. Note that the name of the file is at the top of the snippet: ```typescript import { type RouteConfig } from "@react-router/dev/routes"; import { flatRoutes } from "@react-router/fs-routes"; export default flatRoutes() satisfies RouteConfig; ``` ```typescript import type { Config } from "@react-router/dev/config"; import { reactRouterConfigOptions } from "gadget-server/react-router"; export default reactRouterConfigOptions satisfies Config; ``` ```json // in tsconfig.json { "files": [], "references": [ { "path": "./tsconfig.web.json" }, { "path": "./tsconfig.api.json" } ], "compilerOptions": { "strict": true, "allowJs": true, "checkJs": true, "noImplicitAny": true, "noEmit": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "skipLibCheck": true } } ``` ```json // in tsconfig.web.json { "extends": "./tsconfig.json", "include": [".react-router/types/**/*", "web/**/*", "web/**/.server/**/*", "web/**/.client/**/*"], "compilerOptions": { "composite": true, "strict": true, "lib": ["DOM", "DOM.Iterable", "ES2022"], "types": ["vite/client"], "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", "jsx": "react-jsx", "rootDirs": [".", "./.react-router/types"], "paths": { "@/*": ["./web/*"] }, "esModuleInterop": true, "resolveJsonModule": true } } ``` ```json // in tsconfig.api.json { "extends": "./tsconfig.json", "include": ["api/**/*"], "compilerOptions": { "strict": true, "esModuleInterop": true, "allowJs": true, "checkJs": true, "noImplicitAny": true, "noEmit": true, "sourceMap": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "forceConsistentCasingInFileNames": true, "target": "es2020", "lib": ["es2020", "DOM"], "skipLibCheck": true, "jsx": "react-jsx", "resolveJsonModule": true, "moduleResolution": "node16", "module": "node16" } } ``` Lastly, make sure to add the following to your `.gitignore` file: ```plaintext // in .gitignore .react-router/ ``` Read the [React Router docs](https://reactrouter.com/start/modes) and our [React Router in Gadget docs](https://docs.gadget-canary.xyz/guides/frontend/react-router-in-gadget) for more information on how to use the framework.