# BigCommerce App Extensions  [BigCommerce App Extensions](https://developer.bigcommerce.com/docs/integrations/apps/app-extensions) allow developers to extend the control panel's capabilities by registering custom menu items that appear on select native control panel pages. When a control panel user clicks an App Extension-generated menu item, the app can either render content in a side panel without navigating the user away from the native page, or it can redirect the user to the app's home `iframe` in the Apps sub-menu, and render App Extension-specific content there. Your Gadget frontend routes can be used to power App Extensions. ## Adding a new App Extension  To add a new App Extension to a Gadget app: ### Step 1: Set up a BigCommerce connection  [Set up a BigCommerce connection in Gadget and install on a BigCommerce sandbox store](https://docs.gadget-canary.xyz/guides/getting-started/quickstarts/bigcommerce-quickstart). Make sure that the `App Extensions : manage` OAuth scope is selected when setting up the connection. You don't need to worry about subscribing to webhooks at this time. ### Step 2: Add a field to track the App Extension ID  Add an `appExtensionId` string field to your `bigcommerce/store` model. Each app can install up to 2 App Extensions per store. If you are installing 2 App Extensions, you can use 2 fields or store both IDs in a single string or json field. ### Step 3: Register the App Extension with the store on install  Paste the following code into `api/models/bigcommerce/store/install.js` to create a new App Extension and save the App Extension ID to your `bigcommerce/store` model when your app is installed on a store. Make sure to update the `input` variable of the GraphQL mutation to match the type of App Extension you want to create: ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; export const run: ActionRun = async ({ params, record }) => { applyParams(params, record); await save(record); }; export const onSuccess: ActionOnSuccess = async ({ record, logger, api }) => { const accessToken = ( await api.internal.bigcommerce.store.findFirst({ filter: { storeHash: { equals: record.storeHash } }, }) ).accessToken; // use fetch for GraphQL request (GraphQL not supported by built-in client) const response = await fetch( `https://api.bigcommerce.com/stores/${record.storeHash}/graphql`, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": accessToken, }, body: JSON.stringify({ query: `mutation AppExtension($input: CreateAppExtensionInput!) { appExtension { createAppExtension(input: $input) { appExtension { id context model url label { defaultValue locales { value localeCode } } } } } }`, variables: { // edit input to match your desired App Extension input: { context: "PANEL", model: "PRODUCTS", url: "/products/${id}/interactions", label: { defaultValue: "Interactions", locales: [ { value: "Interaction Notes", localeCode: "en-US", }, { value: "Notas de interacción", localeCode: "es-MX", }, ], }, }, }, }), } ); const jsonResponse = await response.json(); if (jsonResponse.errors) { logger.error({ errors: jsonResponse.errors }, "Error creating app extension"); } // save the App Extension id to your bigcommerce/store model await api.internal.bigcommerce.store.update(record.id, { appExtensionId: jsonResponse.data.appExtension.createAppExtension.appExtension.id, }); }; export const options: ActionOptions = { actionType: "create", }; ``` For production apps, you may also want to run the same code in `api/models/bigcommerce/store/reinstall.js`. ### Step 4: Set up frontend routing  Add a new file to your `web/routes` folder and add a route to your router in `web/components/App.jsx`. For example, if you used the above example for your App Extension and set the `url` to `/products/${id}/interactions`, you would need to add the following to your route definition in `web/components/App.jsx` ```tsx import AppExtension from "../routes/appExtension"; // ... other imports function App() { // add route to router definition const router = createBrowserRouter( createRoutesFromElements( }> {/** ... existing routes */} } /> ) ); return ; } // ... more code ``` Then create a new file at `web/routes/appExtension.jsx` and build your App Extension UI: ```tsx import { Panel, Text } from "@bigcommerce/big-design"; import { useParams } from "react-router"; export default function () { // useParams hook to get route param from BigCommerce const { productId } = useParams(); return ( <> This extension is for product {productId}! ); } ``` The [`useParams` hook](https://reactrouter.com/en/main/hooks/use-params) is used to grab the `productId` included in the route path. ### Step 5: Test your App Extension  Once you finish adding your routes and the custom backend code, you need to: * Uninstall your app from the sandbox. * Delete the existing `bigcommerce/store` record from the Gadget database. This can be done on the `bigcommerce/store` model's data page: `api/models/bigcommerce/store/data`. * Install your app back on the sandbox. This will run your GraphQL mutation on install and register the App Extension. Now you can [navigate to your App Extension](https://developer.bigcommerce.com/docs/integrations/apps/app-extensions#menu-item) and start building. For more information on building frontends, read the [single-click app frontend docs](https://docs.gadget-canary.xyz/guides/plugins/bigcommerce/frontends).