--- title: Products description: >- Product pages allow merchants to create and manage their product catalog, including product details, variants, inventory, pricing, and media. Extensions on these pages help merchants enrich product data, configure bundles, manage subscriptions, or integrate with external systems like PIM or marketplace platforms. api_version: 2025-10 api_name: admin-extensions source_url: html: 'https://shopify.dev/docs/api/admin-extensions/2025-10/targets/products' md: 'https://shopify.dev/docs/api/admin-extensions/2025-10/targets/products.md' --- # Products Product pages allow merchants to create and manage their product catalog, including product details, variants, inventory, pricing, and media. Extensions on these pages help merchants enrich [product data](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections), configure [bundles](https://shopify.dev/docs/apps/build/product-merchandising/bundles), manage [purchase options](https://shopify.dev/docs/apps/build/purchase-options), or integrate with external systems. ### Use cases * **Product data enrichment:** Enhance product information with supplier data, certifications, sustainability metrics, or extended attributes from external product information management (PIM) systems. * **Bundle and kit configuration:** Configure [product bundles](https://shopify.dev/docs/apps/build/product-merchandising/bundles), multi-packs, or kits with component selection, pricing rules, and inventory management across bundle components. * **Subscription and purchase options:** Set up [subscription plans](https://shopify.dev/docs/apps/build/purchase-options/subscriptions), [pre-order options](https://shopify.dev/docs/apps/build/purchase-options/deferred), or custom purchase terms for products through integrated subscription management platforms. * **Marketplace publishing:** Sync [product data](https://shopify.dev/docs/apps/build/product-merchandising/products-and-collections) to external marketplaces like Amazon, eBay, or Google Shopping, including descriptions, pricing, inventory, and marketplace-specific attributes. * **Custom label and document generation:** Generate product labels, barcodes, spec sheets, or compliance documents based on product attributes and external data sources. ![Products targets overview](https://shopify.dev/assets/assets/images/templated-apis-screenshots/admin-extensions/targets-overview-images/admin.product.overview-TTXVtLuJ.png) *** ## Product details targets Use [action and block targets](https://shopify.dev/docs/api/admin-extensions/2025-10#building-your-extension) to extend the product 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 details action target `admin.product-details.action.render` Renders an admin action extension on the product details page. Merchants can access this extension from the **More actions** menu. Use this target to provide workflows that operate on product data, such as syncing with external systems, exporting product information, or managing credit terms. Extensions at this target can access product 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 inventory levels from an external warehouse management system. This example demonstrates calling your app backend to fetch current stock levels and update the product. ##### 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 [updateAll, setUpdateAll] = useState(true); const handleSync = async () => { setLoading(true); setError(null); const productId = 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({ productId, warehouse, updateAllVariants: updateAll, }), }); 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 setUpdateAll(event.currentTarget.checked)} /> Inventory levels will be fetched from your warehouse system. {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add an action extension that publishes a product to an external marketplace using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access). This example demonstrates fetching product details using the \[GraphQL Admin API]\(/docs/api/admin-graphql) and syncing 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 [syncInventory, setSyncInventory] = useState(true); const handlePublish = async () => { setLoading(true); setError(null); const productId = shopify.data.selected[0].id; try { // Fetch product 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 GetProduct($id: ID!) { product(id: $id) { title description status variants(first: 10) { nodes { sku price inventoryQuantity } } } }`, variables: {id: productId}, }), }); const {data} = await response.json(); if (!data?.product) { throw new Error('Product not found'); } // Simulate marketplace API call console.log(`Publishing to ${marketplace}:`, data.product); setSuccess(true); shopify.close(); } catch (err) { setError(err.message || 'Failed to publish product'); } finally { setLoading(false); } }; return ( {success && ( Product successfully published to marketplace! )} {error && ( {error} )} setMarketplace(e.currentTarget.value)} > Amazon eBay Walmart setSyncInventory(e.currentTarget.checked)} /> Product data will be synced to the selected marketplace. {loading ? 'Publishing...' : 'Publish'} shopify.close()}> Cancel ); }; ``` ### Product details action (should render) target `admin.product-details.action.should-render` Controls the render state of an admin action extension on the product details page. Use this target to conditionally show or hide your action extension based on the product'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 with your app backend whether a product is registered in your external catalog system before displaying the action. This example demonstrates calling your app's API endpoint to verify product eligibility. ##### jsx ```jsx export default async () => { const productId = shopify.data.selected[0].id; try { // Check with your app backend if product is in catalog const response = await fetch('https://your-app.com/api/check-catalog', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ productId, checkType: 'catalog-membership', }), }); if (!response.ok) { console.error('Catalog check failed:', response.status); return { display: false }; } const result = await response.json(); // Only show action if product exists in external catalog return { display: result.isInCatalog === true }; } catch (err) { console.error('Error checking catalog status:', err); return { display: false }; } }; ``` * #### ##### Description Add a should-render extension that only displays the action for products that have 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 inventory levels across all variants. ##### jsx ```jsx export default async () => { const productId = 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 GetProductInventory($id: ID!) { product(id: $id) { totalInventory status } }`, variables: {id: productId}, }), }); const {data} = await response.json(); const product = data?.product; // Only show action for active products with available inventory const hasStock = product?.totalInventory > 0; const isActive = product?.status === 'ACTIVE'; return {display: hasStock && isActive}; } catch (err) { console.error('Inventory check failed:', err); return {display: false}; } }; ``` ### Product details block target `admin.product-details.block.render` Renders an admin block extension inline on the product details page. Use this target to display contextual information, analytics, or status updates related to the product without requiring merchant interaction to open a modal. Extensions at this target can access product 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. They provide 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 by fetching data from your app backend. This example demonstrates how to call an external API endpoint and display pricing comparison data in a product details 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 [lastUpdated, setLastUpdated] = useState(null); useEffect(() => { fetchCompetitorPricing(); }, []); const fetchCompetitorPricing = async () => { setLoading(true); setError(false); const productId = 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({productId}), }); if (response.ok) { const data = await response.json(); setCompetitors(data.competitors || []); setLastUpdated(new Date().toLocaleTimeString()); } else { setError(true); } } catch (err) { setError(true); } finally { setLoading(false); } }; return ( {loading && } {error && ( Failed to load competitor pricing data. )} {!loading && !error && competitors.length === 0 && ( No competitor data available for this product. )} {!loading && !error && competitors.length > 0 && ( {competitors.map((competitor, index) => ( {competitor.name} Price: ${competitor.price} {competitor.price < competitor.yourPrice ? ( Lower ) : ( Higher )} ))} Last updated: {lastUpdated} Refresh Prices )} ); }; ``` * #### ##### Description Create a block extension that shows low stock warnings and reorder points for product variants. This example demonstrates using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to fetch inventory levels from the \[GraphQL Admin API]\(/docs/api/admin-graphql) and display actionable 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 [variants, setVariants] = useState([]); const [error, setError] = useState(false); const reorderThreshold = 10; useEffect(() => { fetchInventory(); }, []); const fetchInventory = async () => { const productId = 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 GetInventory($id: ID!) { product(id: $id) { variants(first: 10) { nodes { id displayName inventoryQuantity } } } }`, variables: {id: productId}, }), }); const {data} = await response.json(); setVariants(data?.product?.variants?.nodes || []); } catch (err) { setError(true); } finally { setLoading(false); } }; const lowStockVariants = variants.filter(v => v.inventoryQuantity <= reorderThreshold); if (loading) { return ( ); } return ( {error && ( Failed to load inventory data. )} {!error && lowStockVariants.length === 0 && ( All variants are well stocked! )} {lowStockVariants.length > 0 && ( {lowStockVariants.length} variant(s) below reorder point ({reorderThreshold} units) {lowStockVariants.map((variant) => ( {variant.displayName} {variant.inventoryQuantity} in stock Reorder point: {reorderThreshold} units ))} )} ); }; ``` ### Product details configuration target `admin.product-details.configuration.render` Renders a configuration interface for [product bundles](https://shopify.dev/docs/apps/build/product-merchandising/bundles) on product details pages. This target allows merchants to configure component products, quantities, and pricing for bundle configurations at the product level. 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 Details Configuration API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/contextual-apis/product-details-configuration-api) Examples ### Examples * #### ##### Description Add an action extension that syncs product inventory levels from an external warehouse management system. This example demonstrates calling your app backend to fetch current stock levels and update the product. ##### 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 [updateAll, setUpdateAll] = useState(true); const handleSync = async () => { setLoading(true); setError(null); const productId = 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({ productId, warehouse, updateAllVariants: updateAll, }), }); 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 setUpdateAll(event.currentTarget.checked)} /> Inventory levels will be fetched from your warehouse system. {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add a configuration extension that lets merchants define pricing rules for product bundles, including component discounts and bundle-level adjustments. ##### 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 [syncInventory, setSyncInventory] = useState(true); const [syncPricing, setSyncPricing] = useState(true); const handlePublish = async () => { setLoading(true); setError(null); const productId = shopify.data.selected[0].id; try { // Fetch product 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 GetProduct($id: ID!) { product(id: $id) { title description vendor variants(first: 10) { nodes { sku price inventoryQuantity } } } }`, variables: {id: productId}, }), }); const {data} = await response.json(); if (data?.product) { // Product data ready for marketplace sync setSuccess(true); shopify.close(); } else { setError('Product not found'); } } catch (err) { setError('Failed to publish product'); } finally { setLoading(false); } }; return ( {success && ( Product successfully published to marketplace! )} {error && ( {error} )} setMarketplace(event.currentTarget.value)} > Amazon eBay Walmart setSyncInventory(event.currentTarget.checked)} /> setSyncPricing(event.currentTarget.checked)} /> Product data will be synced to the selected marketplace. {loading ? 'Publishing...' : 'Publish'} shopify.close()}> Cancel ); }; ``` ### Product details print action target `admin.product-details.print-action.render` Renders a print action extension on the product details page that merchants can access from the **Print** menu. Use this target to generate custom printable documents like product labels, barcodes, specification sheets, or compliance documents. Extensions at this target can access the product ID 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) and use the [direct API](https://shopify.dev/docs/api/admin-extensions/2025-10#direct-api-access) to fetch complete product details before generating print output. ### Support Components (45) APIs (1) ### Supported components * [Admin print action](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-print-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 * [Print Action Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/core-apis/print-action-extension-api) Examples ### Examples * #### ##### Description Add an action extension that syncs product inventory levels from an external warehouse management system. This example demonstrates calling your app backend to fetch current stock levels and update the product. ##### 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 [updateAll, setUpdateAll] = useState(true); const handleSync = async () => { setLoading(true); setError(null); const productId = 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({ productId, warehouse, updateAllVariants: updateAll, }), }); 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 setUpdateAll(event.currentTarget.checked)} /> Inventory levels will be fetched from your warehouse system. {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add an action extension that generates a printable product specification sheet with detailed attributes, dimensions, and compliance information using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to fetch complete product data including metafields and variant specifications. ##### 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 [product, setProduct] = useState(null); const [marketplace, setMarketplace] = useState('amazon'); const [success, setSuccess] = useState(false); const [error, setError] = useState(null); useEffect(() => { fetchProduct(); }, []); const fetchProduct = async () => { const productId = 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 GetProduct($id: ID!) { product(id: $id) { title status totalInventory priceRangeV2 { minVariantPrice { amount currencyCode } } } }`, variables: {id: productId}, }), }); const {data} = await response.json(); setProduct(data.product); }; const handlePublish = async () => { setLoading(true); setError(null); try { const response = await fetch('https://your-app.com/api/publish-marketplace', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ productId: shopify.data.selected[0].id, marketplace, productData: product, }), }); if (response.ok) { setSuccess(true); shopify.close(); } else { setError('Failed to publish product'); } } catch (err) { setError('Connection error'); } finally { setLoading(false); } }; return ( {success && ( Product published to {marketplace}! )} {error && ( {error} )} {product ? ( {product.title} Inventory: {product.totalInventory} • Price: {product.priceRangeV2.minVariantPrice.amount} {product.priceRangeV2.minVariantPrice.currencyCode} setMarketplace(e.currentTarget.value)} > Amazon eBay Walmart ) : ( )} {loading ? 'Publishing...' : 'Publish'} shopify.close()}>Cancel ); }; ``` ### Product details print action (should render) target `admin.product-details.print-action.should-render` Controls the render state of an admin action extension on the product details page. Use this target to conditionally show or hide your action extension based on the product'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 with your app backend whether a product has printable labels configured before showing the print action. This example demonstrates calling your app's API to verify print eligibility. ##### jsx ```jsx export default async () => { const productId = shopify.data.selected[0].id; try { const response = await fetch('https://your-app.com/api/check-print-labels', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ productId }), }); if (!response.ok) { console.error('Print label check failed:', response.status); return { display: false }; } const result = await response.json(); return { display: result.hasLabels === true }; } catch (err) { console.error('Error checking print label status:', err); return { display: false }; } }; ``` * #### ##### Description Add a should-render extension that only displays the print action for products that are published and have inventory. 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 product availability. ##### jsx ```jsx export default async () => { const productId = 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 GetProductStatus($id: ID!) { product(id: $id) { status totalInventory publishedAt } }`, variables: { id: productId }, }), }); const { data } = await response.json(); const product = data?.product; const isActive = product?.status === 'ACTIVE'; const isPublished = product?.publishedAt !== null; const hasStock = product?.totalInventory > 0; return { display: isActive && isPublished && hasStock }; } catch (err) { console.error('Error checking product status:', err); return { display: false }; } }; ``` ### Product details reorder target `admin.product-details.reorder.render` Renders a block extension that provides custom reordering functionality on the product details page. This target allows you to display reorder controls, quick reorder buttons, or inventory replenishment workflows directly within the product editor. Use this target when your app needs to help merchants quickly restock or reorder products based on inventory levels or sales velocity. ### 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 Add a reorder extension that lets merchants arrange the display order of product variants. This example demonstrates managing variant sequence using drag-and-drop or manual reordering. ##### 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 productId = 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({ productId, 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(e.currentTarget.value)} > Main Warehouse East Coast DC West Coast DC setUpdateThreshold(e.currentTarget.checked)} /> This will fetch the latest inventory counts from your warehouse system. {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add a reorder extension that lets merchants arrange the display order of product images and videos. This example demonstrates managing media sequence. ##### 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 [markupPercent, setMarkupPercent] = useState('10'); const handlePublish = async () => { setLoading(true); setError(null); const productId = shopify.data.selected[0].id; try { // Fetch product 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 GetProduct($id: ID!) { product(id: $id) { title description status variants(first: 10) { nodes { id sku price inventoryQuantity } } } }`, variables: {id: productId}, }), }); const {data} = await response.json(); if (!data?.product) { throw new Error('Product not found'); } if (data.product.status !== 'ACTIVE') { throw new Error('Only active products can be published'); } // Simulate marketplace API call await new Promise((resolve) => setTimeout(resolve, 800)); setSuccess(true); shopify.close(); } catch (err) { setError(err.message || 'Failed to publish product'); } finally { setLoading(false); } }; return ( {success && ( Product published successfully! )} {error && ( {error} )} setMarketplace(event.currentTarget.value)} > Amazon eBay Walmart setMarkupPercent(event.currentTarget.value)} /> Product will be listed with adjusted pricing on the selected marketplace. {loading ? 'Publishing...' : 'Publish'} shopify.close()}> Cancel ); }; ``` *** ## Product index targets Use [action targets](https://shopify.dev/docs/api/admin-extensions/2025-10#building-your-extension) to extend the product index page with bulk operations and workflows that help merchants manage multiple products efficiently. ### Product index action target `admin.product-index.action.render` Renders an admin action extension on the product index page. Merchants can access this extension from the **More actions** menu. Use this target to provide workflows that operate on product data, such as syncing with external systems, exporting product information, or managing credit terms. Extensions at this target can access product 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 on the products index page to sync inventory levels for multiple products at once from an external warehouse. This example demonstrates batch inventory updates. ##### 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 [updateZeroStock, setUpdateZeroStock] = useState(true); const selectedCount = shopify.data.selected.length; const handleSync = async () => { setLoading(true); setError(null); const productIds = shopify.data.selected.map(item => item.id); try { const response = await fetch('https://your-app.com/api/inventory/sync', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ productIds, warehouse, updateZeroStock, }), }); if (response.ok) { setSuccess(true); shopify.close(); } else { const data = await response.json(); setError(data.message || 'Failed to sync inventory'); } } catch (err) { setError('Connection failed. Please try again.'); } finally { setLoading(false); } }; return ( {success && ( Successfully synced inventory for {selectedCount} product(s)! )} {error && ( {error} )} Syncing {selectedCount} selected product(s) from warehouse system. setWarehouse(e.currentTarget.value)} > Main Warehouse East Distribution Center West Distribution Center setUpdateZeroStock(e.currentTarget.checked)} /> {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add an action extension that publishes selected products to an external marketplace using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to fetch product details from the \[GraphQL Admin API]\(/docs/api/admin-graphql) before syncing. ##### 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 [syncInventory, setSyncInventory] = useState(true); const selectedCount = shopify.data.selected.length; const handlePublish = async () => { setLoading(true); setError(null); try { const productIds = shopify.data.selected.map(item => item.id); // Fetch product 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 GetProducts($ids: [ID!]!) { nodes(ids: $ids) { ... on Product { id title status totalInventory priceRangeV2 { minVariantPrice { amount currencyCode } } } } }`, variables: {ids: productIds}, }), }); const {data} = await response.json(); const products = data.nodes.filter(Boolean); if (products.length === 0) { throw new Error('No valid products found'); } // Sync to marketplace backend await fetch('https://your-app.com/api/marketplace/publish', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({products, marketplace, syncInventory}), }); setSuccess(true); shopify.close(); } catch (err) { setError(err.message || 'Failed to publish products'); } finally { setLoading(false); } }; return ( {success && ( Successfully published {selectedCount} product(s) to {marketplace}! )} {error && ( {error} )} Publishing {selectedCount} selected product(s) setMarketplace(e.currentTarget.value)} > Amazon eBay Walmart Etsy setSyncInventory(e.currentTarget.checked)} /> {loading ? 'Publishing...' : `Publish to ${marketplace}`} shopify.close()}> Cancel ); }; ``` ### Product index action (should render) target `admin.product-index.action.should-render` Controls the render state of an admin action extension on the product index page. Use this target to conditionally show or hide your action extension based on the product'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 selected products are registered in your product catalog system before displaying the action. ##### jsx ```jsx export default async () => { const selectedIds = shopify.data.selected.map(item => item.id); try { // Check with your app backend if products are in your catalog const response = await fetch('https://your-app.com/api/check-catalog-products', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ productIds: selectedIds, }), }); if (!response.ok) { console.error('Catalog check failed:', response.status); return { display: false }; } const result = await response.json(); // Only show action if all selected products are in the catalog return { display: result.allProductsInCatalog }; } catch (err) { console.error('Error checking product catalog:', err); return { display: false }; } }; ``` * #### ##### Description Add an action extension that only displays on the products index page when the store has significant inventory. This example checks total store inventory levels before showing the action. ##### jsx ```jsx export default async () => { const productId = 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 GetProductInventory($id: ID!) { product(id: $id) { totalInventory status } }`, variables: {id: productId}, }), }); const {data} = await response.json(); // Only show action for active products with available inventory const hasStock = data?.product?.totalInventory > 0; const isActive = data?.product?.status === 'ACTIVE'; return {display: hasStock && isActive}; } catch (err) { console.error('Inventory check failed:', err); return {display: false}; } }; ``` ### Product index selection action target `admin.product-index.selection-action.render` Renders a selection action extension on the product index page when merchants select multiple products. Merchants can access this extension from the **More actions** menu. Use this target to provide bulk operations that work on multiple products simultaneously, such as batch tagging, bulk export, price updates, or marketplace publishing. Extensions at this target can access all selected product IDs 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). ### 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 exports selected products to a CSV file or external system. This example demonstrates processing multiple selected products and generating export data. ##### 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 [overwriteManual, setOverwriteManual] = useState(false); const [syncedCount, setSyncedCount] = useState(0); const selectedProducts = shopify.data.selected; const handleSync = async () => { setLoading(true); setError(null); try { const productIds = selectedProducts.map(item => item.id); const response = await fetch('https://your-app.com/api/inventory/sync', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ productIds, overwriteManualAdjustments: overwriteManual, }), }); if (response.ok) { const result = await response.json(); setSyncedCount(result.syncedCount || productIds.length); 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 && ( Successfully synced inventory for {syncedCount} products! )} {error && ( {error} )} {selectedProducts.length} product(s) selected for inventory sync setOverwriteManual(event.currentTarget.checked)} /> {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add an action extension that publishes selected products to an external marketplace using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to fetch product details from the \[GraphQL Admin API]\(/docs/api/admin-graphql) before syncing. ##### 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 [syncInventory, setSyncInventory] = useState(true); const selectedCount = shopify.data.selected.length; const handlePublish = async () => { setLoading(true); setError(null); try { const productIds = shopify.data.selected.map(item => item.id); // Fetch product 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 GetProducts($ids: [ID!]!) { nodes(ids: $ids) { ... on Product { id title status totalInventory priceRangeV2 { minVariantPrice { amount currencyCode } } } } }`, variables: {ids: productIds}, }), }); const {data} = await response.json(); const products = data.nodes.filter(Boolean); if (products.length === 0) { throw new Error('No valid products found'); } // Sync to marketplace backend await fetch('https://your-app.com/api/marketplace/publish', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({products, marketplace, syncInventory}), }); setSuccess(true); shopify.close(); } catch (err) { setError(err.message || 'Failed to publish products'); } finally { setLoading(false); } }; return ( {success && ( Successfully published {selectedCount} product(s) to marketplace! )} {error && ( {error} )} Publishing {selectedCount} selected product(s) setMarketplace(e.currentTarget.value)} > Amazon eBay Walmart Etsy setSyncInventory(e.currentTarget.checked)} /> {loading ? 'Publishing...' : `Publish ${selectedCount} Product(s)`} shopify.close()}> Cancel ); }; ``` ### Product index selection action (should render) target `admin.product-index.selection-action.should-render` Controls the render state of a selection action extension on the product index page. Use this target to conditionally show or hide your action extension based on the product'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 selected products are part of a managed catalog before displaying the action. ##### jsx ```jsx export default async () => { const selectedIds = shopify.data.selected.map(item => item.id); try { // Check with app backend if products are in managed catalog const response = await fetch('https://your-app.com/api/check-catalog-products', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ productIds: selectedIds, }), }); if (!response.ok) { console.error('Backend check failed'); return { display: false }; } const result = await response.json(); // Only show action if all selected products are in the catalog return { display: result.allInCatalog }; } catch (err) { console.error('Error checking catalog status:', err); return { display: false }; } }; ``` * #### ##### Description Add a should-render extension that checks if all selected products have available inventory. This example validates that bulk actions only appear when all selected items meet the criteria. ##### jsx ```jsx export default async () => { const productId = 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 GetProductInventory($id: ID!) { product(id: $id) { totalInventory status } }`, variables: {id: productId}, }), }); const {data} = await response.json(); const product = data?.product; // Only show action for active products with available inventory const hasStock = product?.totalInventory > 0; const isActive = product?.status === 'ACTIVE'; return {display: hasStock && isActive}; } catch (err) { console.error('Failed to check inventory status:', err); return {display: false}; } }; ``` ### Product index selection print action target `admin.product-index.selection-print-action.render` Renders a print action extension on the product index page when merchants select multiple products. Merchants can access this extension from the **Print** menu. Use this target to generate batch print documents like barcode labels, price tags, inventory sheets, or product catalogs for multiple products at once. Extensions at this target can access all selected product IDs 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) and use the [direct API](https://shopify.dev/docs/api/admin-extensions/2025-10#direct-api-access) to fetch complete product details for print generation. ### Support Components (45) APIs (1) ### Supported components * [Admin print action](https://shopify.dev/docs/api/admin-extensions/2025-10/web-components/settings-and-templates/admin-print-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 * [Print Action Extension API](https://shopify.dev/docs/api/admin-extensions/2025-10/target-apis/core-apis/print-action-extension-api) Examples ### Examples * #### ##### Description Add a print action extension that generates barcode labels for selected products. This example demonstrates fetching SKU data and rendering printable barcode labels. ##### 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 [overwriteManual, setOverwriteManual] = useState(false); const selectedCount = shopify.data.selected.length; const handleSync = async () => { setLoading(true); setError(null); const productIds = shopify.data.selected.map(item => item.id); try { const response = await fetch('https://your-app.com/api/inventory/sync', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ productIds, warehouse, overwriteManual, }), }); if (response.ok) { const result = await response.json(); 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 && ( Successfully synced inventory for {selectedCount} product(s)! )} {error && ( {error} )} Syncing {selectedCount} selected product(s) from warehouse system. setWarehouse(event.currentTarget.value)} > Main Warehouse East Distribution Center West Distribution Center setOverwriteManual(event.currentTarget.checked)} /> {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add an action extension that publishes selected products to an external marketplace using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access). This example demonstrates fetching product details using the \[GraphQL Admin API]\(/docs/api/admin-graphql) and preparing them for marketplace listing. ##### 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 [includeInventory, setIncludeInventory] = useState(true); const selectedCount = shopify.data.selected.length; const handlePublish = async () => { setLoading(true); setError(null); try { const productIds = shopify.data.selected.map(item => item.id); const response = await fetch('shopify:admin/api/graphql.json', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ query: `query GetProducts($ids: [ID!]!) { nodes(ids: $ids) { ... on Product { id title status totalInventory priceRangeV2 { minVariantPrice { amount currencyCode } } } } }`, variables: {ids: productIds}, }), }); const {data} = await response.json(); const products = data.nodes.filter(Boolean); console.log(`Publishing ${products.length} products to ${marketplace}`, { includeInventory, products, }); setSuccess(true); shopify.close(); } catch (err) { setError('Failed to publish products. Please try again.'); } finally { setLoading(false); } }; return ( {success && ( Successfully published {selectedCount} product(s) to marketplace! )} {error && ( {error} )} Publishing {selectedCount} selected product(s) setMarketplace(e.currentTarget.value)} > Amazon eBay Walmart Etsy setIncludeInventory(e.currentTarget.checked)} /> {loading ? 'Publishing...' : `Publish to ${marketplace}`} shopify.close()}> Cancel ); }; ``` ### Product index selection print action (should render) target `admin.product-index.selection-print-action.should-render` Controls the render state of an admin action extension on the product index page. Use this target to conditionally show or hide your action extension based on the product'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 shows the print action only for products that have barcodes configured. This example demonstrates checking product data through your app backend to conditionally display the print option. ##### jsx ```jsx export default async () => { const selectedIds = shopify.data.selected.map(item => item.id); try { const response = await fetch('https://your-app.com/api/check-barcodes', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ productIds: selectedIds }), }); if (!response.ok) { console.error('Barcode check failed:', response.status); return { display: false }; } const result = await response.json(); return { display: result.allHaveBarcodes === true }; } catch (err) { console.error('Error checking barcode status:', err); return { display: false }; } }; ``` * #### ##### Description Add a should-render extension that checks if all selected products are active and have available inventory before showing the print action. 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 validate product eligibility. ##### jsx ```jsx export default async () => { const selectedIds = shopify.data.selected.map(item => item.id); const response = await fetch('shopify:admin/api/graphql.json', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ query: `query GetProductStatus($ids: [ID!]!) { nodes(ids: $ids) { ... on Product { id status totalInventory } } }`, variables: {ids: selectedIds}, }), }); const {data} = await response.json(); const allProductsEligible = data?.nodes?.every( product => product?.status === 'ACTIVE' && product?.totalInventory > 0 ); return {display: allProductsEligible}; }; ``` *** ## Product purchase option targets Use [action targets](https://shopify.dev/docs/api/admin-extensions/2025-10#building-your-extension) to extend the product 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 purchase option action target `admin.product-purchase-option.action.render` Renders an admin action extension on the product details page. Merchants can access this extension from the **More actions** menu. Use this target to provide workflows that operate on product data, such as syncing with external systems, exporting product information, or managing credit terms. Extensions at this target can access product 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 syncs product purchase option inventory levels with an external inventory management system. This example demonstrates calling your app backend to update stock quantities and sync subscription or pre-order availability. ##### 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 [syncType, setSyncType] = useState('full'); const [updatePricing, setUpdatePricing] = useState(false); const handleSync = async () => { setLoading(true); setError(null); const purchaseOptionId = 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({ purchaseOptionId, syncType, updatePricing, }), }); if (response.ok) { setSuccess(true); shopify.close(); } else { const data = await response.json(); setError(data.message || 'Sync failed. Please try again.'); } } catch (err) { setError('Connection error. Check your network.'); } finally { setLoading(false); } }; return ( {success && ( Inventory synced successfully! )} {error && ( {error} )} setSyncType(event.currentTarget.value)} > Full sync (all variants) Delta sync (changes only) Availability only setUpdatePricing(event.currentTarget.checked)} /> This will sync inventory levels for subscriptions and pre-orders. {loading ? 'Syncing...' : 'Sync Inventory'} shopify.close()}> Cancel ); }; ``` * #### ##### Description Add an action extension that publishes a product's purchase option (subscription or pre-order) to an external marketplace. This example demonstrates using the \[direct API]\(/docs/api/admin-extensions/2025-10#direct-api-access) to fetch selling plan group details using the \[GraphQL Admin API]\(/docs/api/admin-graphql) before syncing to an external service. ##### 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 [planDetails, setPlanDetails] = useState(null); const [marketplace, setMarketplace] = useState('amazon'); useEffect(() => { fetchPlanDetails(); }, []); const fetchPlanDetails = async () => { const sellingPlanGroupId = 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 GetSellingPlanGroup($id: ID!) { sellingPlanGroup(id: $id) { name merchantCode sellingPlans(first: 5) { nodes { name billingPolicy { ... on SellingPlanRecurringBillingPolicy { interval intervalCount } } } } } }`, variables: {id: sellingPlanGroupId}, }), }); const {data} = await response.json(); setPlanDetails(data.sellingPlanGroup); } catch (err) { setError('Failed to load purchase option details'); } finally { setFetching(false); } }; const handlePublish = async () => { setLoading(true); setError(null); try { // Sync to external marketplace await new Promise(resolve => setTimeout(resolve, 1200)); setSuccess(true); shopify.close(); } catch (err) { setError('Failed to publish to marketplace'); } finally { setLoading(false); } }; if (fetching) { return ( ); } return ( {success && ( Purchase option published to {marketplace}! )} {error && ( {error} )} {planDetails?.name || 'Unknown Plan'} Code: {planDetails?.merchantCode || 'N/A'} {planDetails?.sellingPlans?.nodes?.length || 0} selling plan(s) setMarketplace(event.currentTarget.value)} > Amazon Subscribe & Save Walmart Subscriptions eBay Recurring {loading ? 'Publishing...' : 'Publish'} shopify.close()}> Cancel ); }; ``` *** ## Best practices * **Query only the fields you need:** When fetching product data using [GraphQL](https://shopify.dev/docs/api/admin-graphql), request only the specific fields your extension displays. For example, if you're showing inventory levels, don't fetch media or metafields. This reduces [query costs](https://shopify.dev/docs/api/usage/rate-limits) and improves extension load times. * **Handle product status in workflows:** Before syncing products to external systems or marketplaces, check the product's [status](https://shopify.dev/docs/api/admin-graphql/latest/enums/ProductStatus) (active, draft, archived). Some workflows should only operate on active products, while others may need to handle all statuses differently. * **Validate bundle components before saving:** When building [bundle configuration extensions](https://shopify.dev/docs/apps/build/product-merchandising/bundles/product-configuration-extension/add-merchant-config-ui), verify that all component products are active and have available inventory before allowing merchants to save the bundle configuration. This prevents merchants from creating bundles with unavailable components. * **Consider variant count in displays:** Products with many variants require different UI approaches than simple products. When displaying variant data, implement pagination or collapsible sections for products with many variants to maintain good performance. * **Handle product-level vs variant-level data correctly:** Some data lives at the product level (title, description, media) while other data is variant-specific (SKU, price, inventory). When syncing to external systems, ensure you're pulling data from the correct level to avoid inconsistencies. *** ## 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-purchase-option.action.render` target only appears when the product has a selling plan group associated with it. * **Configuration target availability:** The `admin.product-details.configuration.render` target only appears for products configured as bundles. * **Print menu location:** Print actions appear in the **Print** menu, not the **More actions** menu. * **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. ***