> Deprecated: > 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](/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 points
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: ```typescript?title: '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, ); ``` ```typescript?title: '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 responses in the Shopify admin 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 responses in the Shopify admin 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](/assets/api/app-extensions/argo/create-plan-overlay.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). ```typescript?title: '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); ``` ```typescript?title: '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`](/docs/api/product-subscription-extensions/components/card) - [`Text`](/docs/api/product-subscription-extensions/components/text) - [`TextField`](/docs/api/product-subscription-extensions/components/textfield) - [`InlineStack`](/docs/api/product-subscription-extensions/components/inlinestack) ![App Bridge Admin subscriptions app overlay](/assets/api/app-extensions/argo/create-plan-overlay.png) ```typescript?title: '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); ``` ```typescript?title: '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](/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](/docs/apps/build/purchase-options/product-subscription-app-extensions/authenticate-extension-requests)