---
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: 2026-01
source_url:
  html: >-
    https://shopify.dev/docs/api/admin-extensions/latest/targets/product-variants
  md: >-
    https://shopify.dev/docs/api/admin-extensions/latest/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.

![Shopify admin product variant pages showing all available extension target locations.](https://shopify.dev/assets/assets/images/templated-apis-screenshots/admin-extensions/targets-overview-images/admin.product-variant.overview-wpX8JShe.png)

***

## Product variant details targets

Use [action and block targets](https://shopify.dev/docs/api/admin-extensions/latest#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/latest#direct-api-access), or call your [app's backend](https://shopify.dev/docs/api/admin-extensions/latest#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/latest/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/2026-01/web-components/settings-and-templates/admin-action)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/email-field)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/unordered-list)

### Available APIs

* [Action Extension API](https://shopify.dev/docs/api/admin-extensions/2026-01/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(<Extension />, 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 (
      <s-admin-action heading="Sync Inventory">
        {success && (
          <s-banner tone="success" dismissible={false}>
            Inventory synced successfully from warehouse!
          </s-banner>
        )}
        {error && (
          <s-banner tone="critical" dismissible={false}>
            Failed to sync inventory. Please try again.
          </s-banner>
        )}

        <s-section heading="Warehouse Settings">
          <s-stack gap="base">
            <s-select
              label="Source Warehouse"
              value={warehouse}
              onChange={(event) => setWarehouse(event.currentTarget.value)}
            >
              <s-option value="main">Main Warehouse</s-option>
              <s-option value="east">East Coast DC</s-option>
              <s-option value="west">West Coast DC</s-option>
            </s-select>

            <s-checkbox
              label="Update low stock threshold"
              checked={updateThreshold}
              onChange={(event) => setUpdateThreshold(event.currentTarget.checked)}
            />

            <s-text color="subdued">
              This will fetch the latest inventory count from your warehouse system.
            </s-text>
          </s-stack>
        </s-section>

        <s-button
          slot="primary-action"
          onClick={handleSync}
          disabled={loading || success}
        >
          {loading ? 'Syncing...' : 'Sync Inventory'}
        </s-button>
        <s-button slot="secondary-actions" onClick={() => shopify.close()}>
          Cancel
        </s-button>
      </s-admin-action>
    );
  };
  ```

* ####

  ##### Description

  Add an action extension that publishes a product variant to an external marketplace using the \[direct API]\(/docs/api/admin-extensions/latest#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(<Extension />, 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 (
        <s-admin-action heading="Publish to Marketplace">
          <s-box padding="large"><s-spinner size="base" /></s-box>
        </s-admin-action>
      );
    }

    return (
      <s-admin-action heading="Publish to Marketplace">
        {success && (
          <s-banner tone="success" dismissible={false}>
            Variant published to {marketplace} successfully!
          </s-banner>
        )}
        {error && <s-banner tone="critical" dismissible={false}>{error}</s-banner>}

        <s-section heading="Variant Details">
          <s-stack gap="small">
            <s-text type="strong">{variant?.product?.title}</s-text>
            <s-text color="subdued">Variant: {variant?.title} • SKU: {variant?.sku || 'N/A'}</s-text>
            <s-text>Base Price: ${variant?.price} • Stock: {variant?.inventoryQuantity}</s-text>
          </s-stack>
        </s-section>

        <s-section heading="Marketplace Settings">
          <s-stack gap="base">
            <s-select
              label="Target Marketplace"
              value={marketplace}
              onChange={(e) => setMarketplace(e.currentTarget.value)}
            >
              <s-option value="amazon">Amazon</s-option>
              <s-option value="ebay">eBay</s-option>
              <s-option value="walmart">Walmart</s-option>
            </s-select>
            <s-number-field
              label="Price Markup (%)"
              suffix="%"
              value={markupPercent}
              onChange={(e) => setMarkupPercent(e.currentTarget.value)}
            />
          </s-stack>
        </s-section>

        <s-button slot="primary-action" onClick={handlePublish} disabled={publishing || success}>
          {publishing ? 'Publishing...' : 'Publish Variant'}
        </s-button>
        <s-button slot="secondary-actions" onClick={() => shopify.close()}>
          Cancel
        </s-button>
      </s-admin-action>
    );
  };
  ```

### 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/2026-01/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/latest#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/latest/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/2026-01/web-components/settings-and-templates/admin-block)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/email-field)
* [Form](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/form)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/unordered-list)

### Available APIs

* [Block Extension API](https://shopify.dev/docs/api/admin-extensions/2026-01/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(<Extension />, 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 (
        <s-admin-block heading="Competitor Pricing">
          <s-stack gap="base">
            <s-spinner size="base" />
            <s-text color="subdued">Loading competitor data...</s-text>
          </s-stack>
        </s-admin-block>
      );
    }

    if (error) {
      return (
        <s-admin-block heading="Competitor Pricing">
          <s-banner tone="critical" dismissible={false}>
            Unable to fetch competitor pricing data.
          </s-banner>
        </s-admin-block>
      );
    }

    return (
      <s-admin-block heading="Competitor Pricing">
        <s-stack gap="base">
          <s-box>
            <s-text type="strong">Your price: </s-text>
            <s-text>{variantPrice || '$29.99'}</s-text>
          </s-box>
          
          <s-divider />
          
          <s-section heading="Market Comparison">
            <s-stack gap="small">
              <s-box>
                <s-text>Amazon: $32.99 </s-text>
                <s-badge tone="success">You're lower</s-badge>
              </s-box>
              <s-box>
                <s-text>Walmart: $27.99 </s-text>
                <s-badge tone="warning">$2 cheaper</s-badge>
              </s-box>
              <s-box>
                <s-text>Target: $29.99 </s-text>
                <s-badge>Same price</s-badge>
              </s-box>
            </s-stack>
          </s-section>
          
          <s-text color="subdued">Last updated: 2 hours ago</s-text>
        </s-stack>
      </s-admin-block>
    );
  };
  ```

* ####

  ##### Description

  Create a block extension that shows low stock warnings and reorder points for product variants using the \[direct API]\(/docs/api/admin-extensions/latest#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(<Extension />, 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 (
        <s-admin-block heading="Inventory Alerts">
          <s-box padding="base"><s-spinner size="base" /></s-box>
        </s-admin-block>
      );
    }

    if (error || !inventory) {
      return (
        <s-admin-block heading="Inventory Alerts">
          <s-banner tone="critical" dismissible={false}>
            Unable to load inventory data.
          </s-banner>
        </s-admin-block>
      );
    }

    const status = getStockStatus(inventory.inventoryQuantity);

    return (
      <s-admin-block heading="Inventory Alerts">
        <s-stack gap="base">
          {inventory.inventoryQuantity <= reorderPoint && (
            <s-banner tone={status.tone} dismissible={false}>
              {inventory.inventoryQuantity <= criticalLevel
                ? 'Critical stock level! Immediate reorder required.'
                : 'Stock below reorder point. Consider restocking soon.'}
            </s-banner>
          )}

          <s-box padding="small" background="subdued" borderRadius="base">
            <s-stack gap="small">
              <s-stack gap="small">
                <s-text type="strong">Current Stock</s-text>
                <s-badge tone={status.tone}>{inventory.inventoryQuantity} units</s-badge>
              </s-stack>
              <s-divider />
              <s-stack gap="small">
                <s-text color="subdued">Reorder Point: {reorderPoint} units</s-text>
                <s-text color="subdued">Critical Level: {criticalLevel} units</s-text>
                {inventory.sku && <s-text color="subdued">SKU: {inventory.sku}</s-text>}
              </s-stack>
            </s-stack>
          </s-box>
        </s-stack>
      </s-admin-block>
    );
  };
  ```

### 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/2026-01/web-components/settings-and-templates/admin-block)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/email-field)
* [Form](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/form)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/unordered-list)

### Available APIs

* [Product Variant Details Configuration API](https://shopify.dev/docs/api/admin-extensions/2026-01/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(<Extension />, 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 (
      <s-admin-action heading="Sync Variant Inventory">
        {success && syncResult && (
          <s-banner tone="success" dismissible={false}>
            Inventory synced! New quantity: {syncResult.quantity} units
          </s-banner>
        )}
        {error && (
          <s-banner tone="critical" dismissible={false}>
            {error}
          </s-banner>
        )}

        <s-section heading="Sync Options">
          <s-stack gap="base">
            <s-text color="subdued">
              Pull the latest inventory count from your warehouse system.
            </s-text>
            <s-checkbox
              label="Update backorder availability"
              checked={updateBackorder}
              onChange={(event) => setUpdateBackorder(event.currentTarget.checked)}
            />
          </s-stack>
        </s-section>

        <s-button
          slot="primary-action"
          onClick={handleSync}
          disabled={loading || success}
        >
          {loading ? 'Syncing...' : 'Sync Inventory'}
        </s-button>
        <s-button slot="secondary-actions" onClick={() => shopify.close()}>
          Cancel
        </s-button>
      </s-admin-action>
    );
  };
  ```

* ####

  ##### 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/latest#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(<Extension />, 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 (
        <s-admin-action heading="Publish to Marketplace">
          <s-box padding="large">
            <s-spinner size="base" />
          </s-box>
        </s-admin-action>
      );
    }

    return (
      <s-admin-action heading="Publish to Marketplace">
        {success && (
          <s-banner tone="success" dismissible={false}>
            Variant published to {marketplace} successfully!
          </s-banner>
        )}
        {error && (
          <s-banner tone="critical" dismissible={false}>
            {error}
          </s-banner>
        )}

        <s-section heading="Variant Details">
          <s-stack gap="small">
            <s-text type="strong">{variant.product.title} - {variant.title}</s-text>
            <s-text color="subdued">SKU: {variant.sku || 'Not set'}</s-text>
            <s-text color="subdued">Price: ${variant.price} • Stock: {variant.inventoryQuantity}</s-text>
          </s-stack>
        </s-section>

        <s-section heading="Destination">
          <s-select
            label="Marketplace"
            value={marketplace}
            onChange={(event) => setMarketplace(event.currentTarget.value)}
          >
            <s-option value="amazon">Amazon</s-option>
            <s-option value="ebay">eBay</s-option>
            <s-option value="walmart">Walmart</s-option>
          </s-select>
        </s-section>

        <s-button
          slot="primary-action"
          onClick={handlePublish}
          disabled={loading || success}
        >
          {loading ? 'Publishing...' : 'Publish Variant'}
        </s-button>
        <s-button slot="secondary-actions" onClick={() => shopify.close()}>
          Cancel
        </s-button>
      </s-admin-action>
    );
  };
  ```

***

## Product variant purchase option targets

Use [action targets](https://shopify.dev/docs/api/admin-extensions/latest#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/latest#direct-api-access), or call your [app's backend](https://shopify.dev/docs/api/admin-extensions/latest#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/latest/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/2026-01/web-components/settings-and-templates/admin-action)
* [Avatar](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/avatar)
* [Badge](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/badge)
* [Banner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/banner)
* [Box](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/box)
* [Button](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button)
* [Button group](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/button-group)
* [Checkbox](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/checkbox)
* [Chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/chip)
* [Choice list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/choice-list)
* [Clickable](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable)
* [Clickable chip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/clickable-chip)
* [Color field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-field)
* [Color picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/color-picker)
* [Date field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-field)
* [Date picker](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/date-picker)
* [Divider](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/divider)
* [Drop zone](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/drop-zone)
* [Email field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/email-field)
* [Grid](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/grid)
* [Heading](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/heading)
* [Icon](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/icon)
* [Image](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/image)
* [Link](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/link)
* [Menu](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/actions/menu)
* [Money field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/money-field)
* [Number field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/number-field)
* [Ordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/ordered-list)
* [Paragraph](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/paragraph)
* [Password field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/password-field)
* [Query container](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/query-container)
* [Search field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/search-field)
* [Section](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/section)
* [Select](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/select)
* [Spinner](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/feedback-and-status-indicators/spinner)
* [Stack](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/stack)
* [Switch](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/switch)
* [Table](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/table)
* [Text](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/text)
* [Text area](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-area)
* [Text field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/text-field)
* [Thumbnail](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/media-and-visuals/thumbnail)
* [Tooltip](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/typography-and-content/tooltip)
* [Url field](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/forms/url-field)
* [Unordered list](https://shopify.dev/docs/api/admin-extensions/2026-01/web-components/layout-and-structure/unordered-list)

### Available APIs

* [Purchase Options Card Configuration API](https://shopify.dev/docs/api/admin-extensions/2026-01/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(<Extension />, 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 (
      <s-admin-action heading="Sync Inventory">
        {success && (
          <s-banner tone="success" dismissible={false}>
            Inventory synced successfully!
          </s-banner>
        )}
        {error && (
          <s-banner tone="critical" dismissible={false}>
            {error}
          </s-banner>
        )}

        <s-section heading="Sync Settings">
          <s-stack gap="base">
            <s-select
              label="Source Warehouse"
              value={warehouse}
              onChange={(event) => setWarehouse(event.currentTarget.value)}
            >
              <s-option value="main">Main Warehouse</s-option>
              <s-option value="east">East Coast DC</s-option>
              <s-option value="west">West Coast DC</s-option>
            </s-select>

            <s-checkbox
              label="Update low stock threshold"
              checked={updateThreshold}
              onChange={(event) => setUpdateThreshold(event.currentTarget.checked)}
            />

            <s-text color="subdued">
              This will fetch the latest inventory count from your warehouse system.
            </s-text>
          </s-stack>
        </s-section>

        <s-button
          slot="primary-action"
          onClick={handleSync}
          disabled={loading || success}
        >
          {loading ? 'Syncing...' : 'Sync Inventory'}
        </s-button>
        <s-button slot="secondary-actions" onClick={() => shopify.close()}>
          Cancel
        </s-button>
      </s-admin-action>
    );
  };
  ```

* ####

  ##### 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/latest#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(<Extension />, 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 (
      <s-admin-action heading="Publish to Marketplace">
        {success && (
          <s-banner tone="success" dismissible={false}>
            Variant published successfully!
          </s-banner>
        )}
        {error && (
          <s-banner tone="critical" dismissible={false}>
            {error}
          </s-banner>
        )}

        <s-section heading="Marketplace Settings">
          <s-stack gap="base">
            <s-select
              label="Target marketplace"
              value={marketplace}
              onChange={(e) => setMarketplace(e.currentTarget.value)}
            >
              <s-option value="amazon">Amazon</s-option>
              <s-option value="ebay">eBay</s-option>
              <s-option value="walmart">Walmart</s-option>
            </s-select>

            <s-checkbox
              label="Include subscription options"
              checked={includeSubscription}
              onChange={(e) => setIncludeSubscription(e.currentTarget.checked)}
            />

            <s-text color="subdued">
              Purchase options and pricing will be synced to the selected marketplace.
            </s-text>
          </s-stack>
        </s-section>

        <s-button
          slot="primary-action"
          onClick={handlePublish}
          disabled={loading || success}
        >
          {loading ? 'Publishing...' : 'Publish Variant'}
        </s-button>
        <s-button slot="secondary-actions" onClick={() => shopify.close()}>
          Cancel
        </s-button>
      </s-admin-action>
    );
  };
  ```

***

## 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/latest#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.

***
