Migrate Radio Button from Polaris React
Replace a group of Polaris React RadioButton components with one s-choice-list containing s-choice children. The choice list owns the group label, selected value, error, and keyboard behavior.
Don't migrate a radio button in isolation. Find every radio with the same name or shared state and migrate the complete decision.
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 fields in place during this migration slice.
Anchor to Migrate a radio groupMigrate a radio group
Migrating product visibility radios
Polaris web components
import {useState} from 'react';
export function ProductVisibilityChoice() {
const [visibility, setVisibility] = useState('hidden');
return (
<s-choice-list
label="Product visibility"
name="visibility"
values={[visibility]}
onChange={(event) =>
setVisibility(event.currentTarget.values[0] ?? '')
}
>
<s-choice value="hidden">Hidden</s-choice>
<s-choice value="optional">Optional</s-choice>
<s-choice value="required">Required</s-choice>
</s-choice-list>
);
}Polaris React
import {useState} from 'react';
import {RadioButton} from '@shopify/polaris';
export function ProductVisibility() {
const [visibility, setVisibility] = useState('hidden');
return (
<fieldset>
<legend>Product visibility</legend>
{['hidden', 'optional', 'required'].map((value) => (
<RadioButton
key={value}
id={`visibility-${value}`}
name="visibility"
label={value[0].toUpperCase() + value.slice(1)}
value={value}
checked={visibility === value}
onChange={(_checked) => setVisibility(value)}
/>
))}
</fieldset>
);
}Preview
| Polaris React | Polaris web components | Migration notes |
|---|---|---|
Shared name | name on s-choice-list | Use one stable form field name for the group. |
label | Text content of s-choice | Keep each option label self-contained. |
value | value on s-choice | Use stable backend values, not translated labels. |
checked | values on the containing s-choice-list | For an initial uncontrolled value, set selected on one child. |
onChange(checked, id) | onChange(event) on s-choice-list | Read the single selected value from event.currentTarget.values. |
disabled | disabled on one s-choice or the whole list | Preserve whether one option or the complete decision is unavailable. |
helpText | s-text slot="details" inside that s-choice | Keep option-specific guidance with the option. |
labelHidden | Keep a visible choice label | A radio option generally needs visible text; use an exclusive group label only when nearby context is truly redundant. |
ariaDescribedBy | Choice or list details and error | Prefer documented relationships instead of carrying an external ID forward. |
fill and inherited bleed props | Remove | Let the containing grid or stack own layout. |
tone="magic" | Remove | Preserve meaning in the label and content rather than an unsupported visual treatment. |
Omit multiple so the choice list behaves as a radio group. If merchants can choose more than one value, the old radios modelled the task incorrectly; migrate to a multiple choice list or checkboxes.
A single radio that toggles a boolean isn't a complete radio group. Replace it with s-checkbox when the choice is independently on or off.
Anchor to Reconnect controlled stateReconnect controlled state
For controlled selection, pass an array containing zero or one value to values. On change, read event.currentTarget.values[0] and update the same state used by validation and submission.
Put a required or invalid-selection message on the choice list's error property. Don't attach separate errors to every option when the validation rule applies to the group.
Anchor to Test the migrationTest the migration
- Move through every option with arrow keys and select with keyboard and pointer input.
- Confirm the group never retains more than one value.
- Submit, reload, and edit existing data to verify value serialization.
- Exercise option-level and group-level disabled states.
- Verify group label, option details, required state, and errors are announced together.
Anchor to Remove Polaris ReactRemove Polaris React
After each complete group is migrated, remove RadioButton, shared checked-state adapters, and manual fieldset markup replaced by the choice list. Remove @shopify/polaris only after no other route in scope imports it.