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 migration guide to upgrade your extension.
ChoiceList
The ChoiceList component groups a set of related options as either radio buttons (single selection) or checkboxes (multiple selection). Use it when merchants need to choose from a visible set of options.
For dropdown selection that takes less vertical space, use Select.
Supported targets
- admin.
abandoned-checkout-details. action. render - admin.
abandoned-checkout-details. block. render - admin.
catalog-details. action. render - admin.
catalog-details. block. render - admin.
collection-details. action. render - admin.
collection-details. block. render - admin.
collection-index. action. render - admin.
company-details. action. render - admin.
company-details. block. render - admin.
company-location-details. block. render - admin.
customer-details. action. render - admin.
customer-details. block. render - admin.
customer-index. action. render - admin.
customer-index. selection-action. render - admin.
customer-segment-details. action. render - admin.
discount-details. action. render - admin.
discount-details. function-settings. render - admin.
discount-index. action. render - admin.
draft-order-details. action. render - admin.
draft-order-details. block. render - admin.
draft-order-index. action. render - admin.
draft-order-index. selection-action. render - admin.
gift-card-details. action. render - admin.
gift-card-details. block. render - admin.
order-details. action. render - admin.
order-details. block. render - admin.
order-details. print-action. render - admin.
order-fulfilled-card. action. render - admin.
order-index. action. render - admin.
order-index. selection-action. render - admin.
order-index. selection-print-action. render - admin.
product-details. action. render - admin.
product-details. block. render - admin.
product-details. configuration. render - admin.
product-details. print-action. render - admin.
product-details. reorder. render - admin.
product-index. action. render - admin.
product-index. selection-action. render - admin.
product-index. selection-print-action. render - admin.
product-purchase-option. action. render - admin.
product-variant-details. action. render - admin.
product-variant-details. block. render - admin.
product-variant-details. configuration. render - admin.
product-variant-purchase-option. action. render - admin.
settings. order-routing-rule. render - admin.
settings. validation. render
Supported targets
- admin.
abandoned-checkout-details. action. render - admin.
abandoned-checkout-details. block. render - admin.
catalog-details. action. render - admin.
catalog-details. block. render - admin.
collection-details. action. render - admin.
collection-details. block. render - admin.
collection-index. action. render - admin.
company-details. action. render - admin.
company-details. block. render - admin.
company-location-details. block. render - admin.
customer-details. action. render - admin.
customer-details. block. render - admin.
customer-index. action. render - admin.
customer-index. selection-action. render - admin.
customer-segment-details. action. render - admin.
discount-details. action. render - admin.
discount-details. function-settings. render - admin.
discount-index. action. render - admin.
draft-order-details. action. render - admin.
draft-order-details. block. render - admin.
draft-order-index. action. render - admin.
draft-order-index. selection-action. render - admin.
gift-card-details. action. render - admin.
gift-card-details. block. render - admin.
order-details. action. render - admin.
order-details. block. render - admin.
order-details. print-action. render - admin.
order-fulfilled-card. action. render - admin.
order-index. action. render - admin.
order-index. selection-action. render - admin.
order-index. selection-print-action. render - admin.
product-details. action. render - admin.
product-details. block. render - admin.
product-details. configuration. render - admin.
product-details. print-action. render - admin.
product-details. reorder. render - admin.
product-index. action. render - admin.
product-index. selection-action. render - admin.
product-index. selection-print-action. render - admin.
product-purchase-option. action. render - admin.
product-variant-details. action. render - admin.
product-variant-details. block. render - admin.
product-variant-details. configuration. render - admin.
product-variant-purchase-option. action. render - admin.
settings. order-routing-rule. render - admin.
settings. validation. render
Anchor to PropertiesProperties
Props for the ChoiceList component, which renders a group of selectable choices as either radio buttons (single selection) or checkboxes (multiple selection).
- Anchor to choiceschoiceschoicesChoiceProps[]ChoiceProps[]
The list of choices to render. Each choice is an object with properties like
label,id,disabled,,error, andchecked. Provide at least two choices for meaningful selection.- Anchor to defaultValuedefaultValuedefaultValuestring | string[]string | string[]
The initial value of the field when it isn't controlled by state. Use this instead of
valuewhen you don't need to manage the field's state yourself. The component tracks its own value internally and reports changes through.- Anchor to disableddisableddisabledbooleanbooleanDefault: falseDefault: false
Whether the field is disabled. When
true, the field can't be edited by the user, won't receive focus, and won't be submitted with the form. Use this for fields that aren't relevant in the current context.- Anchor to errorerrorerrorstringstring
An error message to display below the field. When set, the field receives a specific stylistic treatment (typically a red border) to communicate problems that have to be resolved immediately. The string value is displayed as the error message.
Pass
undefinedor omit this prop to clear the error state.- Anchor to multiplemultiplemultiplebooleanbooleanDefault: falseDefault: false
Whether the merchant can select more than one choice. When
true, then each choice is rendered as a checkbox. Whenfalse, then choices are rendered as radio buttons and only one can be selected at a time.- Anchor to namenamenamestringstring
An identifier for the field that is unique within the nearest containing Form component.
- Anchor to onChangeonChangeonChange(value: string | string[]) => void(value: string | string[]) => void
A callback that fires when the user finishes editing the field, typically on blur. Only fires if the value changed. Update your state in this callback and pass the new value back through the
valueprop.This doesn't fire on every keystroke. Use
for real-time responses like clearing validation errors as the user types. Don't useto controlvaluebecause that can cause issues on lower-powered devices due to asynchronous rendering.- Anchor to readOnlyreadOnlyreadOnlybooleanbooleanDefault: falseDefault: false
Whether the field is read-only. Unlike
disabled, a read-only field can still receive focus and its value is included when the form is submitted. Use this when the value should be visible and selectable but not editable, such as a computed total.- Anchor to valuevaluevalueTT
The current value for the field. If omitted, then the field will be empty. You should update this value in response to the
callback.
ChoiceProps
Props for an individual choice within a ChoiceList. Each choice represents a selectable option rendered as either a radio button or a checkbox, depending on the `multiple` setting of the parent ChoiceList.
- accessibilityLabel
A label that describes the purpose or contents of the element. When set, it will be announced to users using assistive technologies and will provide them with more context. When set, any children or `label` supplied won't be announced to screen readers.
string - checked
Whether this individual choice is checked. Use this to pre-select a specific option when the choice list is first rendered.
boolean - disabled
Whether the field is disabled. When `true`, the field can't be edited by the user, won't receive focus, and won't be submitted with the form. Use this for fields that aren't relevant in the current context.
boolean - error
An error message to display below the field. When set, the field receives a specific stylistic treatment (typically a red border) to communicate problems that have to be resolved immediately. The string value is displayed as the error message. Pass `undefined` or omit this prop to clear the error state.
string - id
A unique identifier for the field.
string - label
The text content to display as the field's label. This label is always required for accessibility as it tells users what information the field expects. The label is rendered visually above the field.
string - readOnly
Whether the field is read-only. Unlike `disabled`, a read-only field can still receive focus and its value is included when the form is submitted. Use this when the value should be visible and selectable but not editable, such as a computed total.
boolean
Anchor to ExamplesExamples
Anchor to Choose a shipping methodChoose a shipping method
Pick a shipping speed from standard, express, and overnight radio options. This example renders a ChoiceList with three tiers as radio buttons, and a Button that saves the selected method.
Choose a shipping method
 that saves the selected method.](https://cdn.shopify.com/shopifycloud/shopify-dev/production/assets/assets/images/templated-apis-screenshots/admin-extensions/2025-07/choicelist-default-Br6ss17l.png)
Choose a shipping method
React
import {useState} from 'react';
import {reactExtension, useApi, ChoiceList, Button, BlockStack} from '@shopify/ui-extensions-react/admin';
function App() {
const {data, close} = useApi('admin.product-details.action.render');
const productId = data.selected[0]?.id;
const [shippingMethod, setShippingMethod] = useState('standard');
return (
<BlockStack>
<ChoiceList
name="shippingMethod"
value={shippingMethod}
choices={[
{label: 'Standard shipping (5-7 business days)', id: 'standard'},
{label: 'Express shipping (2-3 business days)', id: 'express'},
{label: 'Overnight delivery', id: 'overnight'},
]}
onChange={setShippingMethod}
/>
<Button
variant="primary"
onPress={async () => {
await fetch('/api/products/shipping-method', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, shippingMethod}),
});
close();
}}
>
Save shipping method
</Button>
</BlockStack>
);
}
export default reactExtension(
'admin.product-details.action.render',
() => <App />,
);TS
import {extension, ChoiceList, Button, BlockStack} from '@shopify/ui-extensions/admin';
export default extension(
'admin.product-details.action.render',
(root, api) => {
const {data, close} = api;
const productId = data.selected[0]?.id;
let shippingMethod = 'standard';
const stack = root.createComponent(BlockStack);
const choiceList = root.createComponent(ChoiceList, {
name: 'shippingMethod',
value: shippingMethod,
choices: [
{label: 'Standard shipping (5-7 business days)', id: 'standard'},
{label: 'Express shipping (2-3 business days)', id: 'express'},
{label: 'Overnight delivery', id: 'overnight'},
],
onChange: (value) => {
shippingMethod = value;
},
});
const saveButton = root.createComponent(
Button,
{
variant: 'primary',
onPress: async () => {
await fetch('/api/products/shipping-method', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, shippingMethod}),
});
close();
},
},
'Save shipping method',
);
stack.appendChild(choiceList);
stack.appendChild(saveButton);
root.appendChild(stack);
},
);Enable multi-select with the multiple prop to let merchants pick several options at once. This example renders checkboxes for product tags like "Seasonal" and "Best seller", collecting an array of selected values to save as product metadata.
Select multiple product tags
React
import {useState} from 'react';
import {reactExtension, useApi, ChoiceList, Button, BlockStack, Text} from '@shopify/ui-extensions-react/admin';
function App() {
const {data, close} = useApi('admin.product-details.action.render');
const productId = data.selected[0]?.id;
const [tags, setTags] = useState([]);
return (
<BlockStack>
<Text fontWeight="bold">Apply product tags</Text>
<ChoiceList
name="productTags"
multiple
value={tags}
choices={[
{label: 'Seasonal', id: 'seasonal'},
{label: 'Clearance', id: 'clearance'},
{label: 'New arrival', id: 'new-arrival'},
{label: 'Best seller', id: 'best-seller'},
{label: 'Limited edition', id: 'limited-edition'},
]}
onChange={setTags}
/>
<Button
variant="primary"
onPress={async () => {
await fetch('/api/products/tags', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, tags}),
});
close();
}}
>
Apply tags
</Button>
</BlockStack>
);
}
export default reactExtension(
'admin.product-details.action.render',
() => <App />,
);TS
import {extension, ChoiceList, Button, BlockStack, Text} from '@shopify/ui-extensions/admin';
export default extension(
'admin.product-details.action.render',
(root, api) => {
const {data, close} = api;
const productId = data.selected[0]?.id;
let tags = [];
const stack = root.createComponent(BlockStack);
const heading = root.createComponent(
Text,
{fontWeight: 'bold'},
'Apply product tags',
);
const choiceList = root.createComponent(ChoiceList, {
name: 'productTags',
multiple: true,
value: tags,
choices: [
{label: 'Seasonal', id: 'seasonal'},
{label: 'Clearance', id: 'clearance'},
{label: 'New arrival', id: 'new-arrival'},
{label: 'Best seller', id: 'best-seller'},
{label: 'Limited edition', id: 'limited-edition'},
],
onChange: (value) => {
tags = value;
},
});
const saveButton = root.createComponent(
Button,
{
variant: 'primary',
onPress: async () => {
await fetch('/api/products/tags', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, tags}),
});
close();
},
},
'Apply tags',
);
stack.appendChild(heading);
stack.appendChild(choiceList);
stack.appendChild(saveButton);
root.appendChild(stack);
},
);Anchor to Validate required selectionValidate required selection
Validate that a selection has been made before submission using the error prop. This example shows an inline error when merchants attempt to save without selecting a compliance region, so a region is always chosen before the data reaches your backend.
Validate required selection
React
import {useState} from 'react';
import {reactExtension, useApi, ChoiceList, Button, BlockStack} from '@shopify/ui-extensions-react/admin';
function App() {
const {data, close} = useApi('admin.product-details.action.render');
const productId = data.selected[0]?.id;
const [region, setRegion] = useState('');
const [error, setError] = useState(undefined);
return (
<BlockStack>
<ChoiceList
name="complianceRegion"
value={region}
error={error}
choices={[
{label: 'North America (FDA, FTC)', id: 'na'},
{label: 'European Union (CE, REACH)', id: 'eu'},
{label: 'Asia-Pacific (JIS, CCC)', id: 'apac'},
]}
onChange={(value) => {
setRegion(value);
setError(undefined);
}}
/>
<Button
variant="primary"
onPress={async () => {
if (!region) {
setError('Select a compliance region before saving');
return;
}
await fetch('/api/products/compliance', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, complianceRegion: region}),
});
close();
}}
>
Set compliance region
</Button>
</BlockStack>
);
}
export default reactExtension(
'admin.product-details.action.render',
() => <App />,
);TS
import {extension, ChoiceList, Button, BlockStack} from '@shopify/ui-extensions/admin';
export default extension(
'admin.product-details.action.render',
(root, api) => {
const {data, close} = api;
const productId = data.selected[0]?.id;
let complianceRegion = '';
const stack = root.createComponent(BlockStack);
const choiceList = root.createComponent(ChoiceList, {
name: 'complianceRegion',
value: complianceRegion,
choices: [
{label: 'North America (FDA, FTC)', id: 'na'},
{label: 'European Union (CE, REACH)', id: 'eu'},
{label: 'Asia-Pacific (JIS, CCC)', id: 'apac'},
],
onChange: (value) => {
complianceRegion = value;
choiceList.updateProps({error: undefined});
},
});
const saveButton = root.createComponent(
Button,
{
variant: 'primary',
onPress: async () => {
if (!complianceRegion) {
choiceList.updateProps({error: 'Select a compliance region before saving'});
return;
}
await fetch('/api/products/compliance', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, complianceRegion}),
});
close();
},
},
'Set compliance region',
);
stack.appendChild(choiceList);
stack.appendChild(saveButton);
root.appendChild(stack);
},
);Anchor to Best practicesBest practices
- Keep the list short and scannable: Display 2-6 options. For longer lists, consider using a Select dropdown instead, which takes up less vertical space.
- Write clear, parallel labels: Each choice label should be written in a consistent format. For example, all labels should start with a verb or all should be noun phrases.
- Provide a default selection for radio buttons: When using single selection, pre-select the most common or recommended option so merchants don't have to make a choice when the default works.
Anchor to LimitationsLimitations
- ChoiceList doesn't support nested or hierarchical choices. All options are presented at the same level.
- Individual choices within the list can be disabled or set to read-only, but the visual distinction between disabled and read-only choices is subtle.
- The
choicesprop requires each choice to have alabeland optionally supportsdisabled,id,readOnly,error, andchecked. Rich content like descriptions or images inside choices isn't supported. - When
multipleistrue, theonChangecallback returns an array of selected values. Whenfalse, it returns a single string value. Your handler must account for both shapes.