---
title: Product variants
description: >-
Product variant pages display information about individual product variations,
including SKU, price, inventory, and option values. Extensions help merchants
manage variant-specific workflows like inventory sync, pricing rules, and
marketplace publishing.
api_version: 2025-10
api_name: admin-extensions
source_url:
html: >-
https://shopify.dev/docs/api/admin-extensions/2025-10/targets/product-variants
md: >-
https://shopify.dev/docs/api/admin-extensions/2025-10/targets/product-variants.md
---
# Product variants
Product variant pages display information about individual [product variations](https://help.shopify.com/manual/products/variants), including SKU, price, inventory levels, and option values like size or color. Extensions on these pages help merchants manage variant-specific workflows, sync inventory with external systems, and configure purchase options.
### Use cases
* **Inventory synchronization:** Sync variant inventory levels with external warehouse management systems, 3PLs, or ERP platforms to maintain accurate stock counts across multiple locations and sales channels.
* **Pricing and cost management:** Display cost information from suppliers, calculate margins, apply bulk pricing rules, or sync variant prices with external pricing engines and wholesale platforms.
* **Marketplace publishing:** Push variant data to external marketplaces like Amazon, eBay, or Google Shopping, including SKU mappings, inventory levels, and marketplace-specific attributes.
* **Subscription and purchase options:** Configure variant-specific subscription settings, [bundle](https://shopify.dev/docs/apps/build/product-merchandising/bundles) configurations, or [pre-order options](https://shopify.dev/docs/apps/build/purchase-options/deferred) through external subscription management platforms.
* **Variant analytics:** Display variant-level performance metrics, sales velocity, or demand forecasting data from external analytics platforms to help merchants optimize inventory.

***
## Product variant details targets
Use [action and block targets](https://shopify.dev/docs/api/admin-extensions/2025-10#building-your-extension) to extend the product variant details page with workflows and contextual information.
Extensions can query and mutate Shopify data using the [direct API](https://shopify.dev/docs/api/admin-extensions/2025-10#direct-api-access), or call your [app's backend](https://shopify.dev/docs/api/admin-extensions/2025-10#app-authentication) for custom business logic and external integrations.
### Product variant details action target
`admin.product-variant-details.action.render`
Renders an admin action extension on the product variants details page. Merchants can access this extension from the **More actions** menu. Use this target to provide workflows that operate on product variants data, such as syncing with external systems, exporting product variants information, or managing credit terms.
Extensions at this target can access product variants data through the `data` property in the [Action Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/core-apis/action-extension-api). The action renders in a modal overlay, providing space for multi-step workflows, forms, and confirmations.
### Support Components (45) APIs (1)
### Supported components
* [Admin action](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-action)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/email-field)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/unordered-list)
### Available APIs
* [Action Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/core-apis/action-extension-api)
Examples
### Examples
* ####
##### Description
Add an action extension that syncs product variant inventory levels from an external warehouse management system. This example demonstrates calling your app backend to fetch real-time stock data and update the variant.
##### jsx
```jsx
import {render} from 'preact';
import {useState} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(false);
const [warehouse, setWarehouse] = useState('main');
const [updateThreshold, setUpdateThreshold] = useState(false);
const handleSync = async () => {
setLoading(true);
setError(false);
const variantId = shopify.data.selected[0].id;
try {
const response = await fetch('https://your-app.com/api/inventory/sync', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
variantId,
warehouse,
updateThreshold,
}),
});
if (response.ok) {
setSuccess(true);
shopify.close();
} else {
setError(true);
}
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};
return (
{success && (
Inventory synced successfully from warehouse!
)}
{error && (
Failed to sync inventory. Please try again.
)}
setWarehouse(event.currentTarget.value)}
>
Main Warehouse
East Coast DC
West Coast DC
setUpdateThreshold(event.currentTarget.checked)}
/>
This will fetch the latest inventory count from your warehouse system.
{loading ? 'Syncing...' : 'Sync Inventory'}
shopify.close()}>
Cancel
);
};
```
* ####
##### Description
Add an action extension that publishes a product variant to an external marketplace using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access). This example demonstrates fetching variant details using the \[GraphQL Admin API]\(/docs/api/admin-graphql) and configuring marketplace listing options before publishing.
##### jsx
```jsx
import {render} from 'preact';
import {useState, useEffect} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(false);
const [publishing, setPublishing] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(null);
const [variant, setVariant] = useState(null);
const [marketplace, setMarketplace] = useState('amazon');
const [markupPercent, setMarkupPercent] = useState('10');
useEffect(() => {
fetchVariantDetails();
}, []);
const fetchVariantDetails = async () => {
setLoading(true);
const variantId = shopify.data.selected[0].id;
const response = await fetch('shopify:admin/api/graphql.json', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: `query GetVariant($id: ID!) {
productVariant(id: $id) {
id
title
sku
price
inventoryQuantity
product { title }
}
}`,
variables: {id: variantId},
}),
});
const {data} = await response.json();
setVariant(data.productVariant);
setLoading(false);
};
const handlePublish = async () => {
setPublishing(true);
setError(null);
try {
const listingPrice = parseFloat(variant.price) * (1 + parseFloat(markupPercent) / 100);
// Simulate marketplace API call
await new Promise(resolve => setTimeout(resolve, 1000));
setSuccess(true);
shopify.close();
} catch (err) {
setError('Failed to publish to marketplace');
} finally {
setPublishing(false);
}
};
if (loading) {
return (
);
}
return (
{success && (
Variant published to {marketplace} successfully!
)}
{error && {error}}
{variant?.product?.title}
Variant: {variant?.title} • SKU: {variant?.sku || 'N/A'}
Base Price: ${variant?.price} • Stock: {variant?.inventoryQuantity}
setMarketplace(e.currentTarget.value)}
>
Amazon
eBay
Walmart
setMarkupPercent(e.currentTarget.value)}
/>
{publishing ? 'Publishing...' : 'Publish Variant'}
shopify.close()}>
Cancel
);
};
```
### Product variant details action (should render) target
`admin.product-variant-details.action.should-render`
Controls the render state of an admin action extension on the product variants details page. Use this target to conditionally show or hide your action extension based on the product variant's properties, such as status, configuration, or specific business requirements.
This target returns a boolean value that determines whether the corresponding action extension appears in the **More actions** menu. The extension evaluates each time the page loads.
### Support Components (0) APIs (1)
### Supported components
\-
### Available APIs
* [Should Render API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/utility-apis/should-render-api)
Examples
### Examples
* ####
##### Description
Add a should-render extension that checks your app backend to determine if a product variant has inventory synchronization enabled before displaying the action.
##### jsx
```jsx
export default async () => {
const variantId = shopify.data.selected[0].id;
try {
// Check with app backend if this variant has inventory sync enabled
const response = await fetch('https://your-app.com/api/check-inventory-sync', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ variantId }),
});
if (!response.ok) {
console.error('Backend check failed:', response.status);
return { display: false };
}
const result = await response.json();
// Only show action if variant is enrolled in inventory sync
return { display: result.syncEnabled === true };
} catch (err) {
console.error('Error checking inventory sync status:', err);
return { display: false };
}
};
```
* ####
##### Description
Add a should-render extension that displays the action only for product variants with stock available. This example uses the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to query the \[GraphQL Admin API]\(/docs/api/admin-graphql) and check the variant's inventory quantity.
##### jsx
```jsx
export default async () => {
const variantId = shopify.data.selected[0].id;
try {
const response = await fetch('shopify:admin/api/graphql.json', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: `query GetVariantInventory($id: ID!) {
productVariant(id: $id) {
inventoryQuantity
inventoryPolicy
}
}`,
variables: {id: variantId},
}),
});
const {data} = await response.json();
const variant = data?.productVariant;
if (!variant) {
return {display: false};
}
// Show action only for variants with available stock
const hasStock = variant.inventoryQuantity > 0;
return {display: hasStock};
} catch (err) {
console.error('Error checking variant inventory:', err);
return {display: false};
}
};
```
### Product variant details block target
`admin.product-variant-details.block.render`
Renders an admin block extension inline on the product variants details page. Use this target to display contextual information, analytics, or status updates related to the product variants without requiring merchant interaction to open a modal.
Extensions at this target can access product variants data through the `data` property in the [Block Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/core-apis/block-extension-api). Blocks appear as cards on the page and can show real-time data, insights, or quick actions, providing persistent visibility for information merchants need to see at a glance.
### Support Components (46) APIs (1)
### Supported components
* [Admin block](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-block)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/email-field)
* [Form](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/form)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/unordered-list)
### Available APIs
* [Block Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/core-apis/block-extension-api)
Examples
### Examples
* ####
##### Description
Create a block extension that shows competitor prices for the current product variant by fetching pricing data from your app backend. This example demonstrates how to call an external API endpoint and display comparison data in a block.
##### jsx
```jsx
import {render} from 'preact';
import {useState, useEffect} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
const [competitors, setCompetitors] = useState([]);
const [variantPrice, setVariantPrice] = useState(null);
useEffect(() => {
fetchCompetitorPricing();
}, []);
const fetchCompetitorPricing = async () => {
const variantId = shopify.data.selected[0].id;
try {
const response = await fetch('https://your-app.com/api/competitor-pricing', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({variantId}),
});
if (response.ok) {
const data = await response.json();
setCompetitors(data.competitors || []);
setVariantPrice(data.yourPrice);
} else {
setError(true);
}
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};
if (loading) {
return (
Loading competitor data...
);
}
if (error) {
return (
Unable to fetch competitor pricing data.
);
}
return (
Your price:
{variantPrice || '$29.99'}
Amazon: $32.99
You're lower
Walmart: $27.99
$2 cheaper
Target: $29.99
Same price
Last updated: 2 hours ago
);
};
```
* ####
##### Description
Create a block extension that shows low stock warnings and reorder points for product variants using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access). This example demonstrates fetching variant inventory data from the \[GraphQL Admin API]\(/docs/api/admin-graphql) and displaying stock level alerts.
##### jsx
```jsx
import {render} from 'preact';
import {useState, useEffect} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(true);
const [inventory, setInventory] = useState(null);
const [error, setError] = useState(false);
const reorderPoint = 10;
const criticalLevel = 5;
useEffect(() => {
fetchInventory();
}, []);
const fetchInventory = async () => {
const variantId = shopify.data.selected[0].id;
try {
const response = await fetch('shopify:admin/api/graphql.json', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: `query GetVariantInventory($id: ID!) {
productVariant(id: $id) {
displayName
sku
inventoryQuantity
inventoryItem {
tracked
}
}
}`,
variables: {id: variantId},
}),
});
const {data} = await response.json();
setInventory(data.productVariant);
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};
const getStockStatus = (quantity) => {
if (quantity <= criticalLevel) return {tone: 'critical', label: 'Critical'};
if (quantity <= reorderPoint) return {tone: 'warning', label: 'Low Stock'};
return {tone: 'success', label: 'In Stock'};
};
if (loading) {
return (
);
}
if (error || !inventory) {
return (
Unable to load inventory data.
);
}
const status = getStockStatus(inventory.inventoryQuantity);
return (
{inventory.inventoryQuantity <= reorderPoint && (
{inventory.inventoryQuantity <= criticalLevel
? 'Critical stock level! Immediate reorder required.'
: 'Stock below reorder point. Consider restocking soon.'}
)}
Current Stock
{inventory.inventoryQuantity} units
Reorder Point: {reorderPoint} units
Critical Level: {criticalLevel} units
{inventory.sku && SKU: {inventory.sku}}
);
};
```
### Product variant details configuration target
`admin.product-variant-details.configuration.render`
Renders a configuration interface for [product bundles](https://shopify.dev/docs/apps/build/product-merchandising/bundles) on product variant details pages. This target allows merchants to configure component products, quantities, and pricing for bundle configurations directly from the variant editor. Use this target when your app needs to provide merchant-facing configuration UI for bundle components and options.
Learn how to add a [product configuration extension](https://shopify.dev/docs/apps/build/product-merchandising/bundles/product-configuration-extension/add-merchant-config-ui).
### Support Components (46) APIs (1)
### Supported components
* [Admin block](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-block)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/email-field)
* [Form](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/form)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/unordered-list)
### Available APIs
* [Product Variant Details Configuration API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/contextual-apis/product-variant-details-configuration-api)
Examples
### Examples
* ####
##### Description
Add a configuration extension that lets merchants define inventory rules for product bundles. This example demonstrates setting up component-level stock requirements and availability thresholds for bundled variants.
##### jsx
```jsx
import {render} from 'preact';
import {useState} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(null);
const [updateBackorder, setUpdateBackorder] = useState(true);
const [syncResult, setSyncResult] = useState(null);
const handleSync = async () => {
setLoading(true);
setError(null);
const variantId = shopify.data.selected[0].id;
try {
const response = await fetch('https://your-app.com/api/inventory/sync', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
variantId,
updateBackorder,
}),
});
if (response.ok) {
const result = await response.json();
setSyncResult(result);
setSuccess(true);
shopify.close();
} else {
const errorData = await response.json();
setError(errorData.message || 'Failed to sync inventory');
}
} catch (err) {
setError('Connection error. Please try again.');
} finally {
setLoading(false);
}
};
return (
{success && syncResult && (
Inventory synced! New quantity: {syncResult.quantity} units
)}
{error && (
{error}
)}
Pull the latest inventory count from your warehouse system.
setUpdateBackorder(event.currentTarget.checked)}
/>
{loading ? 'Syncing...' : 'Sync Inventory'}
shopify.close()}>
Cancel
);
};
```
* ####
##### Description
Add an action extension that publishes a configured product variant to an external marketplace. This example demonstrates using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to fetch variant details from the \[GraphQL Admin API]\(/docs/api/admin-graphql) and sync them to a sales channel.
##### jsx
```jsx
import {render} from 'preact';
import {useState, useEffect} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(false);
const [fetching, setFetching] = useState(true);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(null);
const [variant, setVariant] = useState(null);
const [marketplace, setMarketplace] = useState('amazon');
useEffect(() => {
fetchVariantDetails();
}, []);
const fetchVariantDetails = async () => {
const variantId = shopify.data.selected[0].id;
const response = await fetch('shopify:admin/api/graphql.json', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: `query GetVariant($id: ID!) {
productVariant(id: $id) {
id
title
sku
price
inventoryQuantity
product { title }
}
}`,
variables: {id: variantId}
}),
});
const {data} = await response.json();
setVariant(data.productVariant);
setFetching(false);
};
const handlePublish = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('https://your-app.com/api/publish-variant', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
variantId: variant.id,
sku: variant.sku,
price: variant.price,
inventory: variant.inventoryQuantity,
marketplace
}),
});
if (response.ok) {
setSuccess(true);
shopify.close();
} else {
setError('Failed to publish variant to marketplace');
}
} catch (err) {
setError('Connection error. Please try again.');
} finally {
setLoading(false);
}
};
if (fetching) {
return (
);
}
return (
{success && (
Variant published to {marketplace} successfully!
)}
{error && (
{error}
)}
{variant.product.title} - {variant.title}
SKU: {variant.sku || 'Not set'}
Price: ${variant.price} • Stock: {variant.inventoryQuantity}
setMarketplace(event.currentTarget.value)}
>
Amazon
eBay
Walmart
{loading ? 'Publishing...' : 'Publish Variant'}
shopify.close()}>
Cancel
);
};
```
***
## Product variant purchase option targets
Use [action targets](https://shopify.dev/docs/api/admin-extensions/2025-10#building-your-extension) to extend the product variant purchase option page with workflows and operations.
Extensions can query and mutate Shopify data using the [direct API](https://shopify.dev/docs/api/admin-extensions/2025-10#direct-api-access), or call your [app's backend](https://shopify.dev/docs/api/admin-extensions/2025-10#app-authentication) for custom business logic and external integrations.
### Product variant purchase option action target
`admin.product-variant-purchase-option.action.render`
Renders an admin action extension on the product variants details page. Merchants can access this extension from the **More actions** menu. Use this target to provide workflows that operate on product variants data, such as syncing with external systems, exporting product variants information, or managing credit terms.
Extensions at this target can access product variants data through the `data` property in the [Action Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/core-apis/action-extension-api). The action renders in a modal overlay, providing space for multi-step workflows, forms, and confirmations.
### Support Components (45) APIs (1)
### Supported components
* [Admin action](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-action)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/email-field)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/layout-and-structure/unordered-list)
### Available APIs
* [Purchase Options Card Configuration API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/contextual-apis/purchase-options-card-configuration-api)
Examples
### Examples
* ####
##### Description
Add an action extension that manages inventory allocation for subscription-based variants. This example demonstrates syncing reserved stock quantities for active subscriptions and updating availability for new subscribers.
##### jsx
```jsx
import {render} from 'preact';
import {useState} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(null);
const [warehouse, setWarehouse] = useState('main');
const [updateThreshold, setUpdateThreshold] = useState(false);
const handleSync = async () => {
setLoading(true);
setError(null);
const variantId = shopify.data.selected[0].id;
try {
const response = await fetch('https://your-app.com/api/inventory/sync', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
variantId,
warehouse,
updateThreshold,
}),
});
if (response.ok) {
setSuccess(true);
shopify.close();
} else {
const data = await response.json();
setError(data.message || 'Failed to sync inventory');
}
} catch (err) {
setError('Connection error. Please try again.');
} finally {
setLoading(false);
}
};
return (
{success && (
Inventory synced successfully!
)}
{error && (
{error}
)}
setWarehouse(event.currentTarget.value)}
>
Main Warehouse
East Coast DC
West Coast DC
setUpdateThreshold(event.currentTarget.checked)}
/>
This will fetch the latest inventory count from your warehouse system.
{loading ? 'Syncing...' : 'Sync Inventory'}
shopify.close()}>
Cancel
);
};
```
* ####
##### Description
Add an action extension that publishes a product variant with its purchase option to an external marketplace. This example demonstrates using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to fetch variant details from the \[GraphQL Admin API]\(/docs/api/admin-graphql) and sync them to a sales channel.
##### jsx
```jsx
import {render} from 'preact';
import {useState} from 'preact/hooks';
export default async () => {
render(, document.body);
};
const Extension = () => {
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(null);
const [marketplace, setMarketplace] = useState('amazon');
const [includeSubscription, setIncludeSubscription] = useState(true);
const handlePublish = async () => {
setLoading(true);
setError(null);
const variantId = shopify.data.selected[0].id;
try {
// Fetch variant details using direct API
const response = await fetch('shopify:admin/api/graphql.json', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: `query GetVariant($id: ID!) {
productVariant(id: $id) {
id
title
sku
price
product {
title
}
sellingPlanGroups(first: 5) {
edges {
node {
name
sellingPlans(first: 5) {
edges {
node {
name
billingPolicy {
... on SellingPlanRecurringBillingPolicy {
interval
intervalCount
}
}
}
}
}
}
}
}
}
}`,
variables: {id: variantId},
}),
});
const {data} = await response.json();
const variant = data?.productVariant;
if (!variant) {
throw new Error('Variant not found');
}
// Simulate publishing to marketplace
console.log(`Publishing ${variant.sku} to ${marketplace}`, {
includeSubscription,
sellingPlans: variant.sellingPlanGroups?.edges,
});
setSuccess(true);
shopify.close();
} catch (err) {
setError(err.message || 'Failed to publish variant');
} finally {
setLoading(false);
}
};
return (
{success && (
Variant published successfully!
)}
{error && (
{error}
)}
setMarketplace(e.currentTarget.value)}
>
Amazon
eBay
Walmart
setIncludeSubscription(e.currentTarget.checked)}
/>
Purchase options and pricing will be synced to the selected marketplace.
{loading ? 'Publishing...' : 'Publish Variant'}
shopify.close()}>
Cancel
);
};
```
***
## Best practices
* **Display variant context clearly:** Always show which product a variant belongs to (product title) alongside variant-specific details (option values, SKU). Merchants often view variants out of context and need this information to make decisions.
* **Aggregate inventory across locations:** When displaying variant inventory, show total inventory by default but allow filtering by location. Merchants with [multi-location setups](https://shopify.dev/docs/apps/build/orders-fulfillment/inventory-management-apps) need location-specific visibility for fulfillment decisions.
* **Validate marketplace requirements:** Before publishing variants to external marketplaces, validate that required variant fields (SKU, barcode, weight, dimensions) are populated. Many marketplaces reject variants missing these fields, and early validation prevents failed sync attempts.
* **Handle option combinations carefully:** Variants are defined by [option combinations](https://shopify.dev/docs/api/admin-graphql/latest/objects/ProductVariant#field-ProductVariant.fields.selectedOptions) (for example, Size: Large, Color: Red). When building extensions that manipulate variants, preserve the option structure and validate that option combinations remain unique within the product.
* **Check inventory tracking status:** Use [`inventoryItem.tracked`](https://shopify.dev/docs/api/admin-graphql/latest/objects/InventoryItem#field-InventoryItem.fields.tracked) to determine if a variant tracks inventory before displaying inventory-related actions. Extensions that assume all variants track inventory will fail for digital products or services.
***
## Limitations
* **Single target per module:** Each `[[extensions.targeting]]` entry in your [TOML configuration](https://shopify.dev/docs/api/admin-extensions/2025-10#configuration) maps one target to one module file.
* **Purchase option target visibility:** The `admin.product-variant-purchase-option.action.render` target only appears when the product variant has a [selling plan group](https://shopify.dev/docs/apps/build/purchase-options/subscriptions/selling-plans) associated with it.
* **Configuration target availability:** The `admin.product-variant-details.configuration.render` target only appears for product variants configured as [bundles](https://shopify.dev/docs/apps/build/product-merchandising/bundles).
* **Block target visibility:** Block extensions must be manually [added and pinned](https://help.shopify.com/manual/apps/working-with-apps#add-app-blocks-to-your-shopify-admin) by merchants before they appear.
* **Block collapse behavior:** Returning `null` from a block extension collapses the block rather than removing it from the page. Blocks can't be fully hidden at runtime.
***