Build a customer segment action extension
A customer segment action extension is an action extension that is displayed in the Use segment modal.
This guide is the second part in a two-part tutorial series on how to build a feature using customer segment template extensions and customer segment action extensions in the Use segment modal. The first guide in the series is not required to complete the second part of the tutorial.
This guide demonstrates how to build a customer segment action extension that enables users to perform actions on a target customer segment. This action will appear under the customer segment details page, in the Use segment modal.
Building a customer segment action extension follows the same pattern as building an admin action extension. If you are familiar with building admin actions, then you can skip this tutorial.


Anchor to What you'll learnWhat you'll learn
In this tutorial, you'll learn how to do the following tasks:
-
Create a customer segment action extension that displays on the customer segment details page or the customer segments index page.
-
Fetch information to populate the extensions's initial state.
-
Configure the extension to gather input from users.
-
Update the data using GraphQL based on user input.
-
Run the extension locally and test it on a development store.
Anchor to RequirementsRequirements
-
You've created a Partner account and a development store.
-
Your app is using Shopify CLI 3.48 or higher.
- Your app can make authenticated requests to the GraphQL Admin API.
- Your app has the
read_customers
andwrite_customers
access scopes. Learn how to configure your access scopes using Shopify CLI.
Anchor to Step 1: Create a new extensionStep 1: Create a new extension
To create your customer segment action extension, you can use Shopify CLI to generate a starter extension.
-
Navigate to your app directory:
Terminal
cd <directory> -
Run the following command to create a new admin action extension:
Terminal
shopify app generate extension --template admin_action --name loyalty-points-action --flavor reactThe command creates a new extension template in your app's
extensions
directory with the following structure:Admin action structure
extensions/loyalty-points-action├── README.md├── locales│ ├── en.default.json // The default locale for the extension│ └── fr.json // The French language translations for the extension├── package.json├── shopify.extension.toml // The config file for the extension└── src└── AdminAction.jsx // The code that defines the action's UI and behavior
Anchor to Step 2: Build the extension's UIStep 2: Build the extension's UI
Complete the following steps to build the extension's UI.
Anchor to Review the configurationReview the configuration
The extension's .toml
file stores the extension's static configuration. To have the action appear in the Use segment modal, validate that the target
is set to admin.customer-segment-details.action.render
. For a full list of targets and the locations where they display in the Shopify admin, refer to the admin extension configuration reference.
shopify.extension.toml
To update the display name when users select the action from the menu, in locale files edit the corresponding name
value.
Locales
locales/en.default.json
{
"name": "Reward customers with loyalty points!"
}
{
"name": "Récompensez vos clients avec des points de fidélité!"
}
Anchor to Create the UICreate the UI
This tutorial uses the React implementation of admin UI extensions. If you want to create JavaScript-only extensions, then you'll need to use the provided API from the root object.
Admin UI extensions are rendered using Remote UI, which is a fast and secure remote-rendering framework. Because Shopify renders the UI remotely, components used in the extensions must comply with a contract in the Shopify host. We provide these components through the UI Extensions library. For a list of all the available components and their props, refer to the admin UI extension component reference.
You can view the source of your extension in the src/ActionExtension.jsx
file. This file defines a functional React component that's exported to run at the extension's target. You can create the extension's body by importing and using components from the @shopify/ui-extensions-react/admin
package.
To build your action's UX, add the following code to src/ActionExtension.jsx
, and the locales to locales/en.default.json
.
The extension point in the component export must match the extension point defined in your .toml
file, or the extension won't render.
Creating the UI
import { useCallback, useEffect, useState } from "react";
import {
reactExtension,
useApi,
AdminAction,
BlockStack,
Button,
Text,
NumberField,
} from "@shopify/ui-extensions-react/admin";
import { updateLoyaltyPoints } from "./utils";
// The target used here must match the target used in the extension's toml file (./shopify.extension.toml)
const TARGET = "admin.customer-segment-details.action.render";
export default reactExtension(TARGET, () => <App />);
function App() {
// The useApi hook provides access to several useful APIs like i18n, close, and data.
const { i18n, close, data } = useApi(TARGET);
const [segmentName, setSegmentName] = useState("");
const [loyaltyPoints, setLoyaltyPoints] = useState(0);
// Use direct API calls to fetch data from Shopify.
// See https://shopify.dev/docs/api/admin-graphql for more information about Shopify's GraphQL API
useEffect(() => {
(async function getSegmentInfo() {
const getSegmentQuery = {
query: `query Segment($id: ID!) {
segment(id: $id) {
name
}
}`,
variables: { id: data.selected[0].id },
};
const res = await fetch("shopify:admin/api/graphql.json", {
method: "POST",
body: JSON.stringify(getSegmentQuery),
});
if (!res.ok) {
console.error("Network error");
}
const segmentData = await res.json();
setSegmentName(segmentData.data.segment.name);
})();
}, []);
return (
// The AdminAction component provides an API for setting the title and actions of the Action extension wrapper.
<AdminAction
primaryAction={<Button onPress={() => {
close();
}}>
{i18n.translate("done")}
</Button>}
secondaryAction={
<Button
onPress={() => {
close();
}}
>
{i18n.translate("close")}
</Button>
}
>
<BlockStack gap>
<Text>
{i18n.translate("description", {
segment: segmentName,
})}
</Text>
<NumberField
inputMode="numeric"
max={100}
label={i18n.translate("label")}
value={loyaltyPoints}
onChange={setLoyaltyPoints}
/>
</BlockStack>
</AdminAction>
);
}
{
"name": "Reward customers with loyalty points!",
"description": "Reward customers belonging to the {{ segment }} segment the following points:",
"label": "Loyalty points",
"close": "Close",
"done": "Done"
}
At this point, you can use the developer console to run your app's server and preview your extension. After you preview the extension, changes to your extension's code automatically reload the extension that you're previewing. This enables you to preview your extension and see it update live as you make changes to the code.
Anchor to Step 3: Build the extension logic and connect to the GraphQL Admin APIStep 3: Build the extension logic and connect to the Graph QL Admin API
After defining the UI of the extension, you can complete the experience by building the logic to control it using standard React tooling.
When you're building extensions, you don't need proxy calls to the GraphQL Admin API through your app's backend. Instead, your extension can use direct API access to create requests directly using fetch
. This helps extensions be more performant and responsive for users. This guide includes a utility file for GraphQL queries. Add the contents below to a new file at ./src/utils.js
.
Your app can also get data from the extension APIs, which includes data on the current resource from the data
API.
Extension logic
import { useCallback, useEffect, useState } from "react";
import {
reactExtension,
useApi,
AdminAction,
BlockStack,
Button,
Text,
NumberField,
} from "@shopify/ui-extensions-react/admin";
import { updateLoyaltyPoints } from "./utils";
// The target used here must match the target used in the extension's toml file (./shopify.extension.toml)
const TARGET = "admin.customer-segment-details.action.render";
export default reactExtension(TARGET, () => <App />);
function App() {
// The useApi hook provides access to several useful APIs like i18n, close, and data.
const { i18n, close, data } = useApi(TARGET);
const [segmentName, setSegmentName] = useState("");
const [loyaltyPoints, setLoyaltyPoints] = useState(0);
// Use direct API calls to fetch data from Shopify.
// See https://shopify.dev/docs/api/admin-graphql for more information about Shopify's GraphQL API
useEffect(() => {
(async function getSegmentInfo() {
const getSegmentQuery = {
query: `query Segment($id: ID!) {
segment(id: $id) {
name
}
}`,
variables: { id: data.selected[0].id },
};
const res = await fetch("shopify:admin/api/graphql.json", {
method: "POST",
body: JSON.stringify(getSegmentQuery),
});
if (!res.ok) {
console.error("Network error");
}
const segmentData = await res.json();
setSegmentName(segmentData.data.segment.name);
})();
}, []);
const onSubmit = useCallback(async () => {
// Commit changes to the database
await updateLoyaltyPoints(data.selected[0].id, loyaltyPoints);
// Close the modal using the 'close' API
close();
}, [loyaltyPoints]);
return (
// The AdminAction component provides an API for setting the title and actions of the Action extension wrapper.
<AdminAction
primaryAction={
<Button onPress={onSubmit}>
{i18n.translate("done")}
</Button>
}
secondaryAction={
<Button
onPress={() => {
close();
}}
>
{i18n.translate("close")}
</Button>
}
>
<BlockStack gap>
<Text>
{i18n.translate("description", {
segment: segmentName,
})}
</Text>
<NumberField
inputMode="numeric"
max={100}
label={i18n.translate("label")}
value={loyaltyPoints}
onChange={setLoyaltyPoints}
/>
</BlockStack>
</AdminAction>
);
}
export async function updateLoyaltyPoints(segmentId, points) {
const customerIds = await getCustomerIds(segmentId);
// This example uses metafields to store the data. For more information, refer to https://shopify.dev/docs/apps/custom-data/metafields.
return await makeGraphQLQuery(
`mutation SetMetafield($metafields: [MetafieldsSetInput!]!) {
metafieldDefinitionCreate(
definition: {namespace: "$app:customerLoyalty", key: "points", name: "Loyalty points", ownerType: CUSTOMER, type: "number_integer", access: {admin: MERCHANT_READ_WRITE}}
) {
createdDefinition {
id
}
}
metafieldsSet(metafields: $metafields) {
userErrors {
field
message
code
}
}
}
`,
{
metafields: customerIds.map((customerId) => ({
ownerId: customerId,
namespace: "$app:customerLoyalty",
key: "points",
type: "number_integer",
value: points.toString(),
})),
}
);
}
export async function getCustomerIds(segmentId) {
// This example uses metafields to store the data. For more information, refer to https://shopify.dev/docs/apps/custom-data/metafields.
const response = await makeGraphQLQuery(
`query Segment($id: ID!) {
customerSegmentMembers(first: 10, segmentId: $id) {
edges {
node {
id
}
}
}
}
`,
{ id: segmentId }
);
return response.data.customerSegmentMembers.edges.map((edge) => edge.node.id);
}
async function makeGraphQLQuery(query, variables) {
const graphQLQuery = {
query,
variables,
};
const res = await fetch("shopify:admin/api/graphql.json", {
method: "POST",
body: JSON.stringify(graphQLQuery),
});
if (!res.ok) {
console.error("Network error");
}
return await res.json();
}
{
"name": "Reward customers with loyalty points!",
"description": "Reward customers belonging to the {{ segment }} segment the following points:",
"label": "Loyalty points",
"close": "Close",
"done": "Done"
}
Anchor to Step 4: Test the extensionStep 4: Test the extension
After you've built the extension, you can test it by completing the following steps.
-
Navigate to your app directory:
Terminal
cd <directory> -
To build and preview your app, either start or restart your server with the following command:
Terminal
shopify app dev
- Press
p
to open the developer console. - In the developer console page, click on the preview link for the loyalty points action extension.
The customer segment details page opens. If you don't have a customer segment in your store, then you need to create one.

-
To launch your extension, click the Use segment button and select your extension.
-
After you've entered a number of loyalty points, click Done.
-
Validate that the loyalty points metafield has properly been updated by navigating to one of the customer profiles that belongs to your segment. Click Show all in the Metafields section. The loyalty points metafield should display the correct value.
Anchor to Next stepsNext steps
- Learn about the surfaces in the Shopify admin where you can create admin extensions.
- Learn about the full set of available components for writing admin UI extensions.