---
title: Migrate Select from Polaris React
description: >-
  Replace Polaris React Select option descriptors with s-select and explicit
  s-option children.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/select
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/select.md
api_name: app-home
---

# Migrate Select from Polaris React

Replace Polaris React `Select` with [`s-select`](https://shopify.dev/docs/api/app-home/web-components/forms/select) and explicit `s-option` children. Keep the selected value in form or app state, and preserve the stable values that backend operations expect.

If the app renders this controlled field through React, upgrade to React 19 first. React 18 doesn't provide the custom-element property and event behavior this example relies on. If you can't upgrade yet, leave the controlled Polaris React field in place during this migration slice.

***

## Migrate a select field

The destination moves the `options` descriptor array into child elements and keeps validation on the field.

## Migrating a required shipping-origin field

##### Polaris web components

```html
<s-select
  label="Shipping origin"
  error="Select your primary shipping location to calculate accurate rates for customers"
  required
  value=""
>
  <s-option value="">Select a country</s-option>
  <s-option value="ca">Canada</s-option>
  <s-option value="us">United States</s-option>
  <s-option value="mx">Mexico</s-option>
  <s-option value="uk">United Kingdom</s-option>
</s-select>
```

##### Polaris React

```tsx
import {Select} from '@shopify/polaris';

export function ShippingOrigin({
  selectedOrigin = '',
  validationError = 'Select your primary shipping location to calculate accurate rates for customers',
  onChange,
}) {
  return (
    <Select
      label="Shipping origin"
      requiredIndicator
      value={selectedOrigin}
      error={validationError}
      onChange={onChange}
      options={[
        {label: 'Select a country', value: ''},
        {label: 'Canada', value: 'ca'},
        {label: 'United States', value: 'us'},
        {label: 'Mexico', value: 'mx'},
        {label: 'United Kingdom', value: 'uk'},
      ]}
    />
  );
}
```

***

## Replace option descriptors

| Polaris React | Polaris web components | Migration notes |
| - | - | - |
| `options` | Explicit `s-option` children | Move `label` to child text and keep `value`. |
| Option group | `s-option-group` with `s-option` children | Keep meaningful group labels rather than flattening a long list. |
| Option `disabled` | `disabled` on `s-option` | Preserve unavailable choices without changing their values. |
| `label` | `label` | Keep a visible field label unless nearby context makes it redundant. |
| `labelHidden` | `labelAccessibilityVisibility="exclusive"` | Keep an accessible label. |
| `helpText` | `details` | Keep supporting instructions with the field. |
| `placeholder` | `placeholder` | Use it as a prompt, not as a substitute for the label. |
| `requiredIndicator` | `required` | The property adds semantics and an indicator. Set `error` when validation fails. |
| `value` | `value` | Pass the controlled selected value. |
| `onChange(selected, id)` | `onChange(event)` | Read `event.currentTarget.value`. |
| `error` | `error` | Pass a specific recovery message as a string. |
| `disabled` | `disabled` | Preserve the condition that controls availability. |

Use the same option values before and after migration unless the backend contract is changing deliberately. Changing a display label shouldn't change the serialized value.

***

## Choose the right choice control

Use `s-select` for one compact choice from a list. Use `s-choice-list` when merchants benefit from seeing every option or when choices need individual descriptions. Don't use a select for actions such as **Create**, **Delete**, or **Export**.

For a controlled select, update app state from the change event and pass that value back to the component. For a native form submission, keep a unique `name` and verify that the submitted value matches the backend input.

***

## Test the migration

* Choose every enabled option and verify its stable value reaches app state and form submission.
* Test placeholders, required validation, and field-level errors.
* Verify disabled fields and disabled options can't be selected.
* Test long and localized labels at narrow widths.
* Reload editing forms and confirm the current value restores correctly.

***

## Remove Polaris React

After every call site is migrated, remove `Select`, descriptor-only option builders, and callback adapters. Remove `@shopify/polaris` only after no other route in scope imports it.

***

## Related guidance

* [Select component](https://shopify.dev/docs/api/app-home/web-components/forms/select)
* [Migrate ChoiceList from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/choice-list)
* [Migrate OptionList from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/option-list)

***
