Skip to main content
Migrate to Polaris

Version 2025-07 is the last API version to support React-based UI components. Later versions use web components, native UI elements with built-in accessibility, better performance, and consistent styling with Shopify's design system. Check out the upgrade guide to upgrade your extension.

Customers

Customer pages display information about individual customers, customer lists, and customer segments. Extensions on these pages help merchants enhance customer relationships, manage customer data, and build targeted marketing campaigns.

  • Enhanced customer insights: Display customer insights from external CRM systems, loyalty platforms, or analytics tools to provide merchants with a complete view of customer behavior and preferences.
  • Marketing workflows: Enable merchants to export customer segments to email marketing platforms, create targeted campaigns, or sync customer data with advertising networks for personalized outreach.
  • Loyalty and rewards: Show customer loyalty status, points balances, tier information, or special perks directly within the customer profile to help merchants provide better service.
  • Data quality and verification: Verify customer information, flag duplicate records, enhance customer profiles with additional data from third-party sources, or validate addresses and contact details.
  • Bulk customer operations: Process multiple customers at once for operations like tagging, exporting, updating custom fields, or triggering workflows in external systems.
Shopify admin customer pages showing all available extension target locations.

Anchor to Customer details targetsCustomer details targets

Use action and block targets to extend the customer details page. Add workflows and contextual information that help merchants manage customer relationships and access integrated customer data.

Action targets open as modal overlays from the More actions menu, while block targets display as inline cards. The examples demonstrate fetching data from Shopify's direct API or your app's backend.

admin.customer-details.action.render

Renders an admin action extension on the customer details page. Merchants can access this extension from the More actions menu. Use this target to provide workflows that operate on individual customer data, such as exporting customer information, syncing with CRM systems, or managing loyalty programs.

Extensions at this target can access information about the customer through the data property in the Action Extension API. The action renders in a modal overlay, providing space for multi-step workflows, forms, and confirmations.

Examples
import React from 'react';
import {useState} from 'react';
import {
reactExtension,
useApi,
AdminAction,
Banner,
Section,
BlockStack,
Checkbox,
Button,
} from '@shopify/ui-extensions-react/admin';

const TARGET = 'admin.customer-details.action.render';

export default reactExtension(TARGET, () => <App />);

function App() {
const {data, close, query} = useApi(TARGET);
const [loading, setLoading] = useState(false);
const [syncOrders, setSyncOrders] = useState(true);
const [syncMetafields, setSyncMetafields] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(false);

const handleExport = async () => {
setLoading(true);
setSuccess(false);
setError(false);
const customerId = data.selected[0].id;

try {
// Fetch customer details from GraphQL Admin API
const {data: customerData} = await query(
`
query GetCustomer($id: ID!) {
customer(id: $id) {
id
firstName
lastName
email
phone
ordersCount
amountSpent {
amount
currencyCode
}
tags
addresses {
address1
address2
city
province
country
zip
}
}
}
`,
{variables: {id: customerId}}
);

// Export to CRM through your app's backend
const response = await fetch('https://your-app.com/api/export-customer', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
customer: customerData.customer,
includeOrders: syncOrders,
includeMetafields: syncMetafields,
}),
});

if (response.ok) {
setSuccess(true);
close();
} else {
setError(true);
}
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};

return (
<AdminAction
title="Export to CRM"
primaryAction={
<Button
onPress={handleExport}
disabled={loading || success}
>
{loading ? 'Exporting...' : 'Export Customer'}
</Button>
}
secondaryAction={
<Button onPress={close}>
Cancel
</Button>
}
>
{success && (
<Banner tone="success" dismissible>
Customer exported successfully!
</Banner>
)}
{error && (
<Banner tone="critical" dismissible>
Failed to export customer. Please try again.
</Banner>
)}

<Section heading="Export options">
<BlockStack>
<Checkbox
checked={syncOrders}
onChange={setSyncOrders}
>
Include order history
</Checkbox>
<Checkbox
checked={syncMetafields}
onChange={setSyncMetafields}
>
Include metafield data
</Checkbox>
</BlockStack>
</Section>
</AdminAction>
);
}

Anchor to Customer details action (should render) ,[object Object]Customer details action (should render) target

admin.customer-details.action.should-render

Controls the render state of an admin action extension on the customer details page. Use this target to conditionally show or hide your action extension based on the customer's properties, such as order count, tags, or loyalty status.

This target returns a boolean value that determines whether the corresponding action extension appears in the More actions menu. The extension is evaluated each time the page loads.

Support
Components (0)
APIs (1)

Supported components

-

Available APIs

Examples
import {extension} from '@shopify/ui-extensions/admin';

export default extension(
'admin.customer-details.action.should-render',
async (root, api) => {
const customerId = api.data.selected[0].id;

try {
// Fetch customer tags from GraphQL Admin API
const {data} = await api.query(
`
query GetCustomer($id: ID!) {
customer(id: $id) {
tags
}
}
`,
{variables: {id: customerId}}
);

const tags = data.customer.tags;

// Only show action for customers with VIP tag
return {render: tags.includes('VIP')};
} catch (err) {
console.error('Error fetching customer:', err);
return {render: false};
}
}
);

admin.customer-details.block.render

Renders an admin block extension inline on the customer details page. Use this target to display contextual information, analytics, or status updates related to the customer without requiring merchants to open a modal.

Extensions at this target appear as cards on the page and can show real-time data, insights, or quick actions. Blocks provide persistent visibility and are ideal for displaying information merchants need to see at a glance.

Examples
import React from 'react';
import {useState, useEffect} from 'react';
import {
reactExtension,
useApi,
AdminBlock,
BlockStack,
Box,
Badge,
Heading,
InlineStack,
Text,
Divider,
ProgressIndicator,
} from '@shopify/ui-extensions-react/admin';

const TARGET = 'admin.customer-details.block.render';

export default reactExtension(TARGET, () => <App />);

function App() {
const {data} = useApi(TARGET);
const [loyaltyData, setLoyaltyData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchLoyaltyData = async () => {
const customerId = data.selected[0].id;

try {
// Fetch loyalty data from your app's backend
const response = await fetch(
`https://your-app.com/api/loyalty-info?customerId=${customerId}`
);
const loyaltyInfo = await response.json();
setLoyaltyData(loyaltyInfo);
} catch (err) {
console.error('Error fetching loyalty data:', err);
} finally {
setLoading(false);
}
};

fetchLoyaltyData();
}, [data]);

if (loading) {
return (
<AdminBlock title="Loyalty Status">
<BlockStack>
<ProgressIndicator size="small-100" />
<Text>Loading loyalty information...</Text>
</BlockStack>
</AdminBlock>
);
}

if (!loyaltyData) {
return (
<AdminBlock title="Loyalty Status">
<Text>Unable to load loyalty information</Text>
</AdminBlock>
);
}

return (
<AdminBlock title="Loyalty Status">
<BlockStack>
<Box>
<Badge tone={loyaltyData.tier === 'Platinum' ? 'success' : 'info'}>
{loyaltyData.tier} Tier
</Badge>
</Box>

<Divider />

<Box>
<Heading>Points Balance</Heading>
<InlineStack blockAlignment="center">
<Text fontWeight="bold">{loyaltyData.points.toLocaleString()}</Text>
<Text tone="subdued">points</Text>
</InlineStack>
</Box>

<Divider />

<Box>
<Heading>Lifetime Value</Heading>
<Text>${loyaltyData.lifetimeValue.toLocaleString()}</Text>
</Box>

<Divider />

<Box>
<Heading>Member Since</Heading>
<Text tone="subdued">{loyaltyData.memberSince}</Text>
</Box>

{loyaltyData.nextTier && (
<>
<Divider />
<Box>
<Heading>Progress to {loyaltyData.nextTier}</Heading>
<Text tone="subdued">
Spend ${loyaltyData.nextTierRequired} more to unlock
</Text>
</Box>
</>
)}
</BlockStack>
</AdminBlock>
);
}

Anchor to Customer index targetsCustomer index targets

Use action targets to extend the customer index page with bulk operations and workflows that help merchants manage multiple customers efficiently.

admin.customer-index.action.render

Renders an admin action extension on the customer index page. Merchants can access this extension from the More actions menu. Use this target to provide workflows that operate on the customer list, such as exporting all customers, generating reports, or setting up batch operations.

Extensions at this target can access the page context through the Action Extension API. The action renders in a modal overlay, providing space for configuration and execution of list-wide operations.

Examples
import React from 'react';
import {useState} from 'react';
import {
reactExtension,
useApi,
AdminAction,
Banner,
Section,
BlockStack,
Select,
Button,
} from '@shopify/ui-extensions-react/admin';

const TARGET = 'admin.customer-index.action.render';

export default reactExtension(TARGET, () => <App />);

function App() {
const {close} = useApi(TARGET);
const [loading, setLoading] = useState(false);
const [reportType, setReportType] = useState('summary');
const [dateRange, setDateRange] = useState('30days');
const [success, setSuccess] = useState(false);
const [error, setError] = useState(false);

const reportTypes = [
{value: 'summary', label: 'Customer Summary'},
{value: 'lifetime-value', label: 'Lifetime Value Analysis'},
{value: 'retention', label: 'Retention Report'},
{value: 'segmentation', label: 'Segmentation Analysis'},
];

const dateRanges = [
{value: '7days', label: 'Last 7 days'},
{value: '30days', label: 'Last 30 days'},
{value: '90days', label: 'Last 90 days'},
{value: '1year', label: 'Last year'},
{value: 'all', label: 'All time'},
];

const handleGenerate = async () => {
setLoading(true);
setSuccess(false);
setError(false);

try {
// Generate report through your app's backend
const response = await fetch('https://your-app.com/api/generate-report', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
reportType,
dateRange,
}),
});

if (response.ok) {
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `customer-report-${Date.now()}.pdf`;
a.click();
setSuccess(true);
close();
} else {
setError(true);
}
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};

return (
<AdminAction
title="Generate Customer Report"
primaryAction={
<Button
onPress={handleGenerate}
disabled={loading || success}
>
{loading ? 'Generating...' : 'Generate Report'}
</Button>
}
secondaryAction={
<Button onPress={close}>
Cancel
</Button>
}
>
{success && (
<Banner tone="success" dismissible>
Report generated successfully!
</Banner>
)}
{error && (
<Banner tone="critical" dismissible>
Failed to generate report. Please try again.
</Banner>
)}

<Section heading="Report configuration">
<BlockStack>
<Select
label="Report type"
value={reportType}
onChange={setReportType}
options={reportTypes}
/>

<Select
label="Date range"
value={dateRange}
onChange={setDateRange}
options={dateRanges}
/>
</BlockStack>
</Section>
</AdminAction>
);
}

Anchor to Customer index action (should render) ,[object Object]Customer index action (should render) target

admin.customer-index.action.should-render

Controls the render state of an admin action extension on the customer index page. Use this target to conditionally show or hide your action extension based on business logic, user permissions, or app configuration.

This target returns a boolean value that determines whether the corresponding action extension appears in the More actions menu. The extension is evaluated each time the page loads.

Support
Components (0)
APIs (1)

Supported components

-

Available APIs

Examples
import {extension} from '@shopify/ui-extensions/admin';

export default extension(
'admin.customer-index.action.should-render',
async (root, api) => {
try {
// Check if app is configured through your backend
const response = await fetch(
'https://your-app.com/api/check-configuration'
);
const {configured} = await response.json();

// Only show action if app is configured
return {render: configured};
} catch (err) {
console.error('Error checking configuration:', err);
return {render: false};
}
}
);

admin.customer-index.selection-action.render

Renders a selection action extension on the customer index page when multiple customers are selected. Merchants can access this extension from the More actions menu of the resource list. Use this target to provide bulk operations on selected customers, such as bulk export, bulk tagging, or bulk updates.

Extensions at this target can access the IDs of selected customers through the data property in the Action Extension API. The action renders in a modal overlay designed for batch processing.

Examples
import React from 'react';
import {useState, useEffect} from 'react';
import {
reactExtension,
useApi,
AdminAction,
Banner,
Section,
BlockStack,
Select,
Text,
Button,
} from '@shopify/ui-extensions-react/admin';

const TARGET = 'admin.customer-index.selection-action.render';

export default reactExtension(TARGET, () => <App />);

function App() {
const {data, close, query} = useApi(TARGET);
const [loading, setLoading] = useState(false);
const [customerCount, setCustomerCount] = useState(0);
const [listId, setListId] = useState('');
const [lists, setLists] = useState([]);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(false);

useEffect(() => {
const selectedCustomers = data.selected || [];
setCustomerCount(selectedCustomers.length);

// Fetch available marketing lists
const fetchLists = async () => {
try {
const response = await fetch('https://your-app.com/api/marketing-lists');
const listsData = await response.json();
setLists(listsData.lists.map(list => ({
value: list.id,
label: `${list.name} (${list.subscriberCount} subscribers)`,
})));
if (listsData.lists.length > 0) {
setListId(listsData.lists[0].id);
}
} catch (err) {
console.error('Error fetching lists:', err);
}
};

fetchLists();
}, [data]);

const handleExport = async () => {
setLoading(true);
setSuccess(false);
setError(false);
const customerIds = data.selected.map((customer) => customer.id);

try {
// Fetch customer emails from GraphQL Admin API
const {data: customerData} = await query(
`
query GetCustomers($ids: [ID!]!) {
nodes(ids: $ids) {
... on Customer {
id
email
firstName
lastName
tags
}
}
}
`,
{variables: {ids: customerIds}}
);

// Export to marketing platform through your app's backend
const response = await fetch('https://your-app.com/api/export-to-marketing', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
customers: customerData.nodes,
listId,
}),
});

if (response.ok) {
setSuccess(true);
close();
} else {
setError(true);
}
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};

return (
<AdminAction
title="Export to Marketing Platform"
primaryAction={
<Button
onPress={handleExport}
disabled={loading || success || !listId}
>
{loading ? 'Exporting...' : 'Export Customers'}
</Button>
}
secondaryAction={
<Button onPress={close}>
Cancel
</Button>
}
>
{success && (
<Banner tone="success" dismissible>
{customerCount} customers exported successfully!
</Banner>
)}
{error && (
<Banner tone="critical" dismissible>
Failed to export customers. Please try again.
</Banner>
)}

<Section heading="Export settings">
<BlockStack>
<Text>
Exporting {customerCount} selected customer{customerCount !== 1 ? 's' : ''}
</Text>

<Select
label="Marketing list"
value={listId}
onChange={setListId}
options={lists}
/>
</BlockStack>
</Section>
</AdminAction>
);
}

Anchor to Customer index selection action (should render) ,[object Object]Customer index selection action (should render) target

admin.customer-index.selection-action.should-render

Controls the render state of a selection action extension on the customer index page when multiple customers are selected. Use this target to conditionally show or hide your bulk action extension based on the number of selected customers, their properties, or app configuration.

This target returns a boolean value that determines whether the corresponding selection action extension appears in the More actions menu. The extension is evaluated each time the selection changes.

Support
Components (0)
APIs (1)

Supported components

-

Available APIs

Examples
import {extension} from '@shopify/ui-extensions/admin';

export default extension(
'admin.customer-index.selection-action.should-render',
async (root, api) => {
const selectedCount = api.data.selected?.length || 0;

// Only show action if between 1 and 100 customers are selected
return {render: selectedCount > 0 && selectedCount <= 100};
}
);

Anchor to Customer segment targetsCustomer segment targets

Use action and runnable targets to extend customer segment pages with workflows that help merchants use and export their customer segments for marketing and analysis. Action targets render UI workflows for segment operations, while runnable targets return data to populate pre-built customer segment templates.

admin.customer-segment-details.action.render

Renders an admin action extension on the customer segment details page. Merchants can access this extension from the Use segment button. Use this target to provide workflows that operate on customer segments, such as exporting segments to marketing platforms, creating targeted campaigns, or syncing with external analytics tools.

Extensions at this target can access information about the segment through the data property in the Action Extension API. The action renders in a modal overlay, providing space for segment-specific workflows.

Examples
import React from 'react';
import {useState, useEffect} from 'react';
import {
reactExtension,
useApi,
AdminAction,
Banner,
Section,
BlockStack,
Box,
Heading,
TextField,
Text,
Button,
ProgressIndicator,
} from '@shopify/ui-extensions-react/admin';

const TARGET = 'admin.customer-segment-details.action.render';

export default reactExtension(TARGET, () => <App />);

function App() {
const {data, close, query} = useApi(TARGET);
const [loading, setLoading] = useState(false);
const [fetching, setFetching] = useState(true);
const [segmentName, setSegmentName] = useState('');
const [customerCount, setCustomerCount] = useState(0);
const [audienceName, setAudienceName] = useState('');
const [success, setSuccess] = useState(false);
const [error, setError] = useState(false);

useEffect(() => {
const fetchSegmentDetails = async () => {
const segmentId = data.selected[0].id;

try {
// Fetch segment details from GraphQL Admin API
const {data: segmentData} = await query(
`
query GetSegment($id: ID!) {
segment(id: $id) {
name
query
}
}
`,
{variables: {id: segmentId}}
);

setSegmentName(segmentData.segment.name);
setAudienceName(segmentData.segment.name);

// Get customer count from your app's backend
const countResponse = await fetch(
`https://your-app.com/api/segment-count?segmentId=${segmentId}`
);
const countData = await countResponse.json();
setCustomerCount(countData.count);
} catch (err) {
console.error('Error fetching segment:', err);
} finally {
setFetching(false);
}
};

fetchSegmentDetails();
}, [data, query]);

const handleExport = async () => {
setLoading(true);
setSuccess(false);
setError(false);
const segmentId = data.selected[0].id;

try {
// Export segment through your app's backend
const response = await fetch('https://your-app.com/api/export-segment', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
segmentId,
audienceName,
}),
});

if (response.ok) {
setSuccess(true);
close();
} else {
setError(true);
}
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};

if (fetching) {
return (
<AdminAction title="Export to Email Platform">
<BlockStack>
<ProgressIndicator size="small-100" />
<Text>Loading segment details...</Text>
</BlockStack>
</AdminAction>
);
}

return (
<AdminAction
title="Export to Email Platform"
primaryAction={
<Button
onPress={handleExport}
disabled={loading || success || !audienceName.trim()}
>
{loading ? 'Exporting...' : 'Create Audience'}
</Button>
}
secondaryAction={
<Button onPress={close}>
Cancel
</Button>
}
>
{success && (
<Banner tone="success" dismissible>
Segment exported successfully! Audience "{audienceName}" created.
</Banner>
)}
{error && (
<Banner tone="critical" dismissible>
Failed to export segment. Please try again.
</Banner>
)}

<Section heading="Segment information">
<BlockStack>
<Box>
<Heading>Source segment</Heading>
<Text>{segmentName}</Text>
</Box>

<Box>
<Heading>Customer count</Heading>
<Text>{customerCount.toLocaleString()} customers</Text>
</Box>

<TextField
label="Audience name"
value={audienceName}
onChange={setAudienceName}
/>
</BlockStack>
</Section>
</AdminAction>
);
}

Anchor to Customer segment details action (should render) ,[object Object]Customer segment details action (should render) target

admin.customer-segment-details.action.should-render

Controls the render state of an admin action extension on the customer segment details page. Use this target to conditionally show or hide your action extension based on the segment's properties, such as customer count, segment query complexity, or app configuration.

This target returns a boolean value that determines whether the corresponding action extension appears in the Use segment button menu. The extension is evaluated each time the page loads.

Support
Components (0)
APIs (1)

Supported components

-

Available APIs

Examples
import {extension} from '@shopify/ui-extensions/admin';

export default extension(
'admin.customer-segment-details.action.should-render',
async (root, api) => {
const segmentId = api.data.selected[0].id;

try {
// Check segment size through your app's backend
const response = await fetch(
`https://your-app.com/api/segment-count?segmentId=${segmentId}`
);
const {count} = await response.json();

// Only show action if segment has at least 10 customers
return {render: count >= 10};
} catch (err) {
console.error('Error checking segment size:', err);
return {render: false};
}
}
);

admin.customers.segmentation-templates.render

Use this target to provide a customer segments template extension that returns an array of templates in the customer segment editor. The templates include a title, description, query, and optional dependencies.

This target provides merchants with pre-built customer segment queries for common use cases like identifying VIP customers, finding at-risk customers, or creating cohorts based on purchase behavior.

Extensions at this target use the Customer Segment Template Extension API to return template data. Templates appear in the segment editor's template gallery and can be inserted with a single click.

Support
Components (1)
APIs (0)

Supported components

Available APIs

-
Examples
import {
reactExtension,
CustomerSegmentTemplate,
} from '@shopify/ui-extensions-react/admin';

const TARGET = 'admin.customers.segmentation-templates.render';

export default reactExtension(TARGET, () => <App />);

function App() {
return (
<>
<CustomerSegmentTemplate
title="High Lifetime Value Customers"
description="Identify your most valuable customers based on total spending. Perfect for VIP programs and exclusive promotions."
query="amount_spent > 1000"
/>
<CustomerSegmentTemplate
title="Frequent Buyers"
description="Find customers who have purchased multiple times and are likely to buy again."
query="number_of_orders >= 5 AND last_order_date >= -90 days"
/>
<CustomerSegmentTemplate
title="Recent High-Value Customers"
description="Target high-value customers who have purchased recently. Perfect for loyalty programs and exclusive offers."
query="amount_spent > 500 AND last_order_date >= -30 days"
/>
</>
);
}

  • Respect customer privacy: Customer data is sensitive. Always handle customer information securely, comply with privacy regulations (GDPR, CCPA), and only request the data your extension truly needs to function.
  • Handle email consent properly: When exporting customers for marketing purposes, always respect email marketing consent status and provide merchants with information about consent requirements to maintain compliance.
  • Support segmentation use cases: When creating customer segment templates, focus on actionable segments that help merchants make business decisions. Include clear descriptions explaining when and why to use each template.

  • Single target per module: Each [[extensions.targeting]] entry in your TOML configuration maps one target to one module file.
  • Segment template query validation: Customer segment template queries aren't validated at deployment. Invalid queries fail silently when merchants use the template. Test your template queries in the segment editor before deploying them to ensure they work correctly and return expected results.
  • Segment action location: The admin.customer-segment-details.action.render target appears under the Use segment button, not in More actions.
  • Block target visibility: Block extensions must be manually added and pinned 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.

Was this page helpful?