Migrate Setting Toggle from Polaris React
Replace Polaris React SettingToggle with s-switch when the setting applies immediately. Use s-checkbox when the value is part of a larger form that merchants save explicitly.
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 setting in place during this migration slice.
Anchor to Migrate an immediate settingMigrate an immediate setting
Migrating an enabled setting
Polaris web components
import {useRef, useState} from 'react';
export function CustomerAccountsSetting({initialEnabled, persistEnabled}) {
const [enabled, setEnabled] = useState(initialEnabled);
const [pending, setPending] = useState(false);
const confirmedValue = useRef(initialEnabled);
const latestRequest = useRef(0);
async function handleChange(event) {
const nextEnabled = event.currentTarget.checked;
const request = ++latestRequest.current;
setEnabled(nextEnabled);
setPending(true);
try {
await persistEnabled(nextEnabled);
if (request === latestRequest.current) {
confirmedValue.current = nextEnabled;
shopify.toast.show('Customer account setting saved');
}
} catch {
if (request === latestRequest.current) {
setEnabled(confirmedValue.current);
shopify.toast.show("Customer account setting couldn't be saved", {
isError: true,
});
}
} finally {
if (request === latestRequest.current) setPending(false);
}
}
return (
<s-stack gap="small">
<s-switch
label="Customer accounts"
details={`Customer accounts are ${enabled ? 'enabled' : 'disabled'}.`}
checked={enabled}
onChange={handleChange}
/>
{pending && <s-text color="subdued">Saving setting…</s-text>}
</s-stack>
);
}Polaris React
import {useState} from 'react';
import {SettingToggle} from '@shopify/polaris';
export function CustomerAccountsSetting() {
const [enabled, setEnabled] = useState(true);
return (
<SettingToggle
enabled={enabled}
action={{
content: enabled ? 'Disable' : 'Enable',
onAction: () => setEnabled((current) => !current),
}}
>
Customer accounts are {enabled ? 'enabled' : 'disabled'}.
</SettingToggle>
);
}Preview
Anchor to Replace SettingToggle responsibilitiesReplace Setting Toggle responsibilities
| Polaris React | Polaris web components | Migration notes |
|---|---|---|
enabled | checked on s-switch | Drive it from the last confirmed value. |
action.content | Switch label, such as Customer accounts | A switch label names the setting, not the next action. |
action.onAction | onChange(event) | Read event.currentTarget.checked and persist it. |
action.loading | Adjacent pending status or temporarily disabled switch | Prevent overlapping writes and make progress visible. |
action.disabled | disabled | Explain why the setting is unavailable. |
children | details or visible text beside the switch | Keep consequences and prerequisites visible. |
For immediate persistence, keep the previous confirmed value until the request succeeds or optimistically update and roll back on failure. In either approach, reject stale responses when merchants toggle rapidly. Show an error toast or banner and leave a clear retry path.
For settings submitted together, use a named s-checkbox inside a native form and connect it to the same save bar as the other fields. Don't mix one immediate switch into an explicitly saved form without making that difference clear.
Anchor to Test the migrationTest the migration
- Toggle on and off through pending, success, failure, rollback, and retry.
- Toggle rapidly and verify stale responses don't overwrite the latest intent.
- Reload to confirm the backend value is authoritative.
- Exercise disabled permissions and prerequisites with visible explanation.
- Verify label, details, checked state, and errors are announced.
Anchor to Remove Polaris ReactRemove Polaris React
Remove SettingToggle, action descriptor builders, and duplicate enabled state after persistence uses the switch or form model. Remove @shopify/polaris only after no other route in scope imports it.