BigCommerce 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.
Add an appExtensionIdstring 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:
api/models/bigcommerce/store/install.js
JavaScript
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",
};
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
web/components/App.jsx
React
import AppExtension from "../routes/appExtension";
// ... other imports
function App() {
// add route to router definition
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<Layout />}>
{/** ... existing routes */}
<Route path="/products/:productId/interactions" element={<AppExtension />} />
</Route>
)
);
return <RouterProvider router={router} />;
}
// ... more code
import AppExtension from "../routes/appExtension";
// ... other imports
function App() {
// add route to router definition
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<Layout />}>
{/** ... existing routes */}
<Route path="/products/:productId/interactions" element={<AppExtension />} />
</Route>
)
);
return <RouterProvider router={router} />;
}
// ... more code
Then create a new file at web/routes/appExtension.jsx and build your App Extension UI:
web/routes/appExtension.jsx
React
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 (
<>
<Panel description="Successfully created an App Extension!">
<Text>This extension is for product {productId}!</Text>
</Panel>
</>
);
}
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 (
<>
<Panel description="Successfully created an App Extension!">
<Text>This extension is for product {productId}!</Text>
</Panel>
</>
);
}
The useParams hook 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.