--- 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)**: ![App Bridge Admin subscriptions app overlay](https://shopify.dev/assets/assets/images/api/app-extensions/argo/create-plan-overlay-DhJTHs-l.png) 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) ![App Bridge Admin subscriptions app overlay](https://shopify.dev/assets/assets/images/api/app-extensions/argo/create-plan-overlay-DhJTHs-l.png) ## 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)