Extension points
Product subscription app extensions won't be supported as of December 3, 2025. You should migrate existing product subscription app extensions to purchase options extensions.
You've generated a product subscription app extension, and you now have a project folder with the extension script (either ./index.ts(x)
or ./index.js
). This guide describes the extension points in the script and how the script renders data and UI components.
Anchor to Extension pointsExtension points
Each extension point is triggered by a different merchant action, receives different data, and is responsible for handling a distinct part of the subscription experience.
The product subscription app extension uses the following extension points:
The extension points must be rendered separately.
Extension point | Mode | Description |
---|---|---|
Admin::Product::SubscriptionPlan::Add | Add | Add an existing purchase option to a product or variant. |
Admin::Product::SubscriptionPlan::Create | Create | Create a new purchase option |
Admin::Product::SubscriptionPlan::Edit | Edit | Edit an existing purchase option |
Admin::Product::SubscriptionPlan::Remove | Remove | Remove an existing purchase option from a product or variant |
Anchor to ExampleExample
The following example shows how to render the extension points in JavaScript and React:
src/index.js
import {extend} from '@shopify/admin-ui-extensions';
function Add(root, api) {
root.appendChild(root.createText('Hello, world'));
root.mount();
}
function Create() {
/* ... */
}
function Edit() {
/* ... */
}
function Remove() {
/* ... */
}
extend(
'Admin::Product::SubscriptionPlan::Add',
Add,
);
extend(
'Admin::Product::SubscriptionPlan::Create',
Create,
);
extend(
'Admin::Product::SubscriptionPlan::Edit',
Edit,
);
extend(
'Admin::Product::SubscriptionPlan::Remove',
Remove,
);
import {extend, render, Text} from '@shopify/admin-ui-extensions-react';
function Add() {
return <Text>Hello, world</Text>;
}
function Create() {
/* ... */
}
function Edit() {
/* ... */
}
function Remove() {
/* ... */
}
extend(
'Admin::Product::SubscriptionPlan::Add',
render(() => <Add />),
);
extend(
'Admin::Product::SubscriptionPlan::Create',
render(() => <Create />),
);
extend(
'Admin::Product::SubscriptionPlan::Edit',
render(() => <Edit />),
);
extend(
'Admin::Product::SubscriptionPlan::Remove',
render(() => <Remove />),
);
Anchor to Expected responsesExpected responses
The response of an extension point will depend on the context in which it was triggered. Here are the expected responses:
Anchor to Product details pageProduct details page
Extension point | Expected reponse |
---|---|
Admin::Product::SubscriptionPlan::Add |
|
Admin::Product::SubscriptionPlan::Create |
|
Admin::Product::SubscriptionPlan::Edit |
|
Admin::Product::SubscriptionPlan::Remove |
|
Anchor to Variant details pageVariant details page
Extension point | Expected reponse |
---|---|
Admin::Product::SubscriptionPlan::Add |
|
Admin::Product::SubscriptionPlan::Create |
|
Admin::Product::SubscriptionPlan::Edit |
|
Admin::Product::SubscriptionPlan::Remove |
|
Anchor to Data renderingData rendering
Your extension receives data from the host page. The Create
callback function includes the data passed into the host page.
Anchor to ExampleExample
In the following example, the current product is being rendered inside the extension as (Product 1):

In the first line of the Create
callback function, the data
variable is assigned to the input data that's passed into the extension from the host page.
- In vanilla JavaScript, input data is passed into the
Create
callback function. Refer to the JavaScript product subscription app extension template. - In React, input data is passed using the
useData
hook. Refer to the React product subscription app extension template.
src/index.js
import {extend, Card} from '@shopify/admin-ui-extensions';
function Create(root, api) {
const data = api.data;
// ...
const planTitleCard = root.createComponent(Card, {
sectioned: true,
title: `Create subscription plan for Product id ${data.productId}`,
});
root.appendChild(planTitleCard);
root.mount();
}
extend('Admin::Product::SubscriptionPlan::Create', Create);
import {extend, render, useData, Card} from '@shopify/admin-ui-extensions-react';
function Create() {
const data = useData();
// ...
return (
// ...
<Card
title={`Create subscription plan for Product id ${data.productId}`}
sectioned
> ... </Card>
)
}
extend('Admin::Product::SubscriptionPlan::Create', render(() => <Create />);
Anchor to UI componentsUI components
The Create
callback function renders the UI components that appear in the canvas of the app overlay.
Anchor to ExampleExample
The example renders the following UI components:

src/index.js
import {extend, Card, Text, TextField, InlineStack} from '@shopify/admin-ui-extensions';
function Create(api, root) {
// ...
const planDetailsCard = root.createComponent(Card, {
sectioned: true,
title: 'Delivery and discount',
});
root.appendChild(planDetailsCard);
const inlineStack = root.createComponent(InlineStack);
planDetailsCard.appendChild(inlineStack);
const deliveryFrequencyField = root.createComponent(TextField, {
type: 'number',
label: 'Delivery frequency (in weeks)',
value: undefined,
onChange(value) {
deliveryFrequencyField.updateProps({
value,
});
},
});
inlineStack.appendChild(deliveryFrequencyField);
const percentageOffField = root.createComponent(TextField, {
type: 'number',
label: 'Percentage off (%)',
value: undefined,
onChange(value) {
percentageOffField.updateProps({
value,
});
},
});
inlineStack.appendChild(percentageOffField);
const actionsElement = root.createComponent(InlineStack, {distribution: 'fill'});
root.appendChild(actionsElement);
actionsElement.appendChild(secondaryButton);
const primaryButtonStack = root.createComponent(InlineStack, {
distribution: 'trailing',
});
actionsElement.appendChild(primaryButtonStack);
primaryButtonStack.appendChild(primaryButton);
root.mount();
}
extend('Admin::Product::SubscriptionPlan::Create', Create);
import {extend, render, Card, Text, TextField, InlineStack} from '@shopify/admin-ui-extensions-react';
function Create() {
// ...
return (
<>
<Text size="titleLarge">Create plan</Text>
<Card title={`Create subscription plan for Product id ${data.productId}`} sectioned>
<TextField label="Plan title" value={planTitle} onAfterChange={setPlanTitle} />
</Card>
<Card title="Delivery and discount" sectioned>
<InlineStack>
<TextField
type="number"
label="Delivery frequency (in weeks)"
value={deliveryFrequency}
onAfterChange={setDeliveryFrequency}
/>
<TextField
type="number"
label="Percentage off (%)"
value={percentageOff}
onAfterChange={setPercentageOff}
/>
</InlineStack>
</Card>
{actions}
);
}
extend('Admin::Product::SubscriptionPlan::Create', render(() => <Create />);
For more information, refer to the following resources:
- UI components for the product subscription app extension
- JavaScript product subscription app extension template
- React product subscription app extension template