---
title: Extension points
description: Learn about the extension points in the product subscription extension script.
source_url:
html: >-
https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points
md: >-
https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md
---
ExpandOn this page
* [Extension points](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#extension-points)
* [Data rendering](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#data-rendering)
* [UI components](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#ui-components)
* [Next steps](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#next-steps)
# Extension points
Deprecated
Product subscription app extensions won't be supported as of February 9, 2026. You should migrate existing product subscription app extensions to [purchase options extensions](https://shopify.dev/docs/apps/build/purchase-options/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.
***
## Extension 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:
Note
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 |
### Example
The following example shows how to render the extension points in JavaScript and React:
## src/index.js
```typescript
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,
);
```
```typescript
import {extend, render, Text} from '@shopify/admin-ui-extensions-react';
function Add() {
return Hello, world;
}
function Create() {
/* ... */
}
function Edit() {
/* ... */
}
function Remove() {
/* ... */
}
extend(
'Admin::Product::SubscriptionPlan::Add',
render(() => ),
);
extend(
'Admin::Product::SubscriptionPlan::Create',
render(() => ),
);
extend(
'Admin::Product::SubscriptionPlan::Edit',
render(() => ),
);
extend(
'Admin::Product::SubscriptionPlan::Remove',
render(() => ),
);
```
##### JavaScript
```
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,
);
```
##### React
```
import {extend, render, Text} from '@shopify/admin-ui-extensions-react';
function Add() {
return Hello, world;
}
function Create() {
/* ... */
}
function Edit() {
/* ... */
}
function Remove() {
/* ... */
}
extend(
'Admin::Product::SubscriptionPlan::Add',
render(() => ),
);
extend(
'Admin::Product::SubscriptionPlan::Create',
render(() => ),
);
extend(
'Admin::Product::SubscriptionPlan::Edit',
render(() => ),
);
extend(
'Admin::Product::SubscriptionPlan::Remove',
render(() => ),
);
```
### Expected responses
The response of an extension point will depend on the context in which it was triggered. Here are the expected responses:
#### Product details page
| Extension point | Expected reponse |
| - | - |
| `Admin::Product::SubscriptionPlan::Add` | * `productId`: The `id` of the current product
* `variantId`: `null` |
| `Admin::Product::SubscriptionPlan::Create` | - `productId`: The `id` of the current product
- `variantId`: `null` |
| `Admin::Product::SubscriptionPlan::Edit` | * `sellingPlanGroupId`: The `id` of the selling plan group being edited
* `productId`: The `id` of the current product
* `variantId`: `null` |
| `Admin::Product::SubscriptionPlan::Remove` | - `sellingPlanGroupId`: The `id` of the selling plan group being edited
- `productId`: The `id` of the current product
- `variantId`: `null`
- `variantIds`: An array of the current product's child variant `ids` for which you should also remove the selling plan group association |
#### Variant details page
| Extension point | Expected reponse |
| - | - |
| `Admin::Product::SubscriptionPlan::Add` | * `productId`: The `id` of the current variant's parent product
* `variantId`: The `id` of the current variant |
| `Admin::Product::SubscriptionPlan::Create` | - `productId`: The `id` of the current variant's parent product
- `variantId`: The `id` of the current variant |
| `Admin::Product::SubscriptionPlan::Edit` | * `sellingPlanGroupId`: The `id` of the selling plan group being edited
* `productId`: The `id` of the current variant's parent product
* `variantId`: The `id` of the current variant |
| `Admin::Product::SubscriptionPlan::Remove` | - `sellingPlanGroupId`: The `id` of the selling plan group being edited
- `productId`: The `id` of the current variant's parent product
- `variantId`: The `id` of the current variant
- `variantIds`: An empty array because the variant has no child variants |
***
## Data rendering
Your extension receives data from the host page. The `Create` callback function includes the data passed into the host page.
### Example
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](https://github.com/Shopify/admin-ui-extensions-template/blob/main/scripts/generate/templates/PRODUCT_SUBSCRIPTION/vanilla.template.js).
* In React, input data is passed using the `useData` hook. Refer to the [React product subscription app extension template](https://github.com/Shopify/admin-ui-extensions-template/blob/main/scripts/generate/templates/PRODUCT_SUBSCRIPTION/react.template.js).
## src/index.js
```typescript
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);
```
```typescript
import {extend, render, useData, Card} from '@shopify/admin-ui-extensions-react';
function Create() {
const data = useData();
// ...
return (
// ...
...
)
}
extend('Admin::Product::SubscriptionPlan::Create', render(() => );
```
##### JavaScript
```
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);
```
##### React
```
import {extend, render, useData, Card} from '@shopify/admin-ui-extensions-react';
function Create() {
const data = useData();
// ...
return (
// ...
...
)
}
extend('Admin::Product::SubscriptionPlan::Create', render(() => );
```
***
## UI components
The `Create` callback function renders the UI components that appear in the canvas of the app overlay.
### Example
The example renders the following UI components:
* [`Card`](https://shopify.dev/docs/api/product-subscription-extensions/components/card)
* [`Text`](https://shopify.dev/docs/api/product-subscription-extensions/components/text)
* [`TextField`](https://shopify.dev/docs/api/product-subscription-extensions/components/textfield)
* [`InlineStack`](https://shopify.dev/docs/api/product-subscription-extensions/components/inlinestack)

## src/index.js
```typescript
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);
```
```typescript
import {extend, render, Card, Text, TextField, InlineStack} from '@shopify/admin-ui-extensions-react';
function Create() {
// ...
return (
<>
Create plan
{actions}
);
}
extend('Admin::Product::SubscriptionPlan::Create', render(() => );
```
##### JavaScript
```
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);
```
##### React
```
import {extend, render, Card, Text, TextField, InlineStack} from '@shopify/admin-ui-extensions-react';
function Create() {
// ...
return (
<>
Create plan
{actions}
);
}
extend('Admin::Product::SubscriptionPlan::Create', render(() => );
```
For more information, refer to the following resources:
* [UI components for the product subscription app extension](https://shopify.dev/docs/api/product-subscription-extensions/components)
* [JavaScript product subscription app extension template](https://github.com/Shopify/admin-ui-extensions-template/blob/main/scripts/generate/templates/PRODUCT_SUBSCRIPTION/vanilla.template.js)
* [React product subscription app extension template](https://github.com/Shopify/admin-ui-extensions-template/blob/main/scripts/generate/templates/PRODUCT_SUBSCRIPTION/react.template.js)
***
## Next steps
* [Authenticate requests between your extension and app server](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/authenticate-extension-requests)
***
* [Extension points](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#extension-points)
* [Data rendering](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#data-rendering)
* [UI components](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#ui-components)
* [Next steps](https://shopify.dev/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points.md#next-steps)