# 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).