BigCommerce App Extensions
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.
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. 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:
1import { applyParams, save, ActionOptions } from "gadget-server";23export const run: ActionRun = async ({ params, record }) => {4 applyParams(params, record);5 await save(record);6};78export const onSuccess: ActionOnSuccess = async ({ record, logger, api }) => {9 const accessToken = (10 await api.internal.bigcommerce.store.findFirst({11 filter: { storeHash: { equals: record.storeHash } },12 })13 ).accessToken;1415 // use fetch for GraphQL request (GraphQL not supported by built-in client)16 const response = await fetch(17 `https://api.bigcommerce.com/stores/${record.storeHash}/graphql`,18 {19 method: "POST",20 headers: {21 "Content-Type": "application/json",22 Accept: "application/json",23 "X-Auth-Token": accessToken,24 },25 body: JSON.stringify({26 query: `mutation AppExtension($input: CreateAppExtensionInput!) {27 appExtension {28 createAppExtension(input: $input) {29 appExtension {30 id31 context32 model33 url34 label {35 defaultValue36 locales {37 value38 localeCode39 }40 }41 }42 }43 }44 }`,45 variables: {46 // edit input to match your desired App Extension47 input: {48 context: "PANEL",49 model: "PRODUCTS",50 url: "/products/${id}/interactions",51 label: {52 defaultValue: "Interactions",53 locales: [54 {55 value: "Interaction Notes",56 localeCode: "en-US",57 },58 {59 value: "Notas de interacción",60 localeCode: "es-MX",61 },62 ],63 },64 },65 },66 }),67 }68 );6970 const jsonResponse = await response.json();7172 if (jsonResponse.errors) {73 logger.error({ errors: jsonResponse.errors }, "Error creating app extension");74 }7576 // save the App Extension id to your bigcommerce/store model77 await api.internal.bigcommerce.store.update(record.id, {78 appExtensionId:79 jsonResponse.data.appExtension.createAppExtension.appExtension.id,80 });81};8283export const options: ActionOptions = {84 actionType: "create",85};
1import { applyParams, save, ActionOptions } from "gadget-server";23export const run: ActionRun = async ({ params, record }) => {4 applyParams(params, record);5 await save(record);6};78export const onSuccess: ActionOnSuccess = async ({ record, logger, api }) => {9 const accessToken = (10 await api.internal.bigcommerce.store.findFirst({11 filter: { storeHash: { equals: record.storeHash } },12 })13 ).accessToken;1415 // use fetch for GraphQL request (GraphQL not supported by built-in client)16 const response = await fetch(17 `https://api.bigcommerce.com/stores/${record.storeHash}/graphql`,18 {19 method: "POST",20 headers: {21 "Content-Type": "application/json",22 Accept: "application/json",23 "X-Auth-Token": accessToken,24 },25 body: JSON.stringify({26 query: `mutation AppExtension($input: CreateAppExtensionInput!) {27 appExtension {28 createAppExtension(input: $input) {29 appExtension {30 id31 context32 model33 url34 label {35 defaultValue36 locales {37 value38 localeCode39 }40 }41 }42 }43 }44 }`,45 variables: {46 // edit input to match your desired App Extension47 input: {48 context: "PANEL",49 model: "PRODUCTS",50 url: "/products/${id}/interactions",51 label: {52 defaultValue: "Interactions",53 locales: [54 {55 value: "Interaction Notes",56 localeCode: "en-US",57 },58 {59 value: "Notas de interacción",60 localeCode: "es-MX",61 },62 ],63 },64 },65 },66 }),67 }68 );6970 const jsonResponse = await response.json();7172 if (jsonResponse.errors) {73 logger.error({ errors: jsonResponse.errors }, "Error creating app extension");74 }7576 // save the App Extension id to your bigcommerce/store model77 await api.internal.bigcommerce.store.update(record.id, {78 appExtensionId:79 jsonResponse.data.appExtension.createAppExtension.appExtension.id,80 });81};8283export const options: ActionOptions = {84 actionType: "create",85};
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
1import AppExtension from "../routes/appExtension";23// ... other imports45function App() {6 // add route to router definition7 const router = createBrowserRouter(8 createRoutesFromElements(9 <Route path="/" element={<Layout />}>10 {/** ... existing routes */}11 <Route path="/products/:productId/interactions" element={<AppExtension />} />12 </Route>13 )14 );1516 return <RouterProvider router={router} />;17}1819// ... more code
1import AppExtension from "../routes/appExtension";23// ... other imports45function App() {6 // add route to router definition7 const router = createBrowserRouter(8 createRoutesFromElements(9 <Route path="/" element={<Layout />}>10 {/** ... existing routes */}11 <Route path="/products/:productId/interactions" element={<AppExtension />} />12 </Route>13 )14 );1516 return <RouterProvider router={router} />;17}1819// ... more code
Then create a new file at web/routes/appExtension.jsx
and build your App Extension UI:
1import { Panel, Text } from "@bigcommerce/big-design";2import { useParams } from "react-router";34export default function () {5 // useParams hook to get route param from BigCommerce6 const { productId } = useParams();78 return (9 <>10 <Panel description="Successfully created an App Extension!">11 <Text>This extension is for product {productId}!</Text>12 </Panel>13 </>14 );15}
1import { Panel, Text } from "@bigcommerce/big-design";2import { useParams } from "react-router";34export default function () {5 // useParams hook to get route param from BigCommerce6 const { productId } = useParams();78 return (9 <>10 <Panel description="Successfully created an App Extension!">11 <Text>This extension is for product {productId}!</Text>12 </Panel>13 </>14 );15}
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 thebigcommerce/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 and start building.
For more information on building frontends, read the single-click app frontend docs.