Skip to main content

Migrate Checkbox from Polaris React

Replace Polaris React Checkbox with s-checkbox. Keep the checked value in app state when other controls or backend operations depend on it, and use the field's native form properties when the value is submitted with a form.

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.


This example preserves a required acknowledgement and its visible validation message.

Migrating a required acknowledgement

import {useState} from 'react';

export function TermsCheckbox() {
const [accepted, setAccepted] = useState(false);

return (
<s-checkbox
label="I agree to the terms"
checked={accepted}
error={accepted ? undefined : 'You must accept the terms to continue'}
onChange={(event) => setAccepted(event.currentTarget.checked)}
/>
);
}
import {Checkbox} from '@shopify/polaris';

export function TermsCheckbox({
accepted = false,
error = 'You must accept the terms to continue',
onChange,
}) {
return (
<Checkbox
label="I agree to the terms"
checked={accepted}
error={error}
onChange={onChange}
/>
);
}

Preview


Anchor to Map checkbox propertiesMap checkbox properties

Polaris ReactPolaris web componentsMigration notes
labellabelPass a string that describes the choice. Don't move the only label into surrounding text.
labelHiddenVisible label, or accessibilityLabel when adjacent content already identifies the controls-checkbox doesn't expose labelAccessibilityVisibility. Prefer a visible label.
helpTextdetailsKeep instructions next to the checkbox.
errorerrorPass the validation message as a string. A boolean error state without a message isn't sufficient.
checked={true}checkedUse for controlled state. Read the next value from event.currentTarget.checked.
checked="indeterminate"indeterminateKeep checked and indeterminate as separate values. Indeterminate changes appearance, not the submitted value.
disableddisabledPreserve the reason the choice can't be changed. Don't use disabled state to hide an authorization failure.
id, name, and valueid, name, and valueKeep stable form names and backend values.
onChange(checked, id)onChange(event)Read event.currentTarget.checked and use the component's known ID when the handler needs it.

Use defaultChecked instead of checked only when the checkbox is intentionally uncontrolled. Don't combine defaultChecked with React state that expects to update the rendered value.

Like a native checkbox, an unchecked s-checkbox contributes no entry to FormData. If the backend must receive an explicit false value, normalize the missing key on the server or append a fallback value in the submit handler. Don't add a hidden input with the same name unless the backend intentionally supports receiving both values when the checkbox is checked.


Anchor to Preserve partial selectionPreserve partial selection

For a select-all checkbox, calculate these values from the selected resource IDs:

  1. Set checked when every item in the defined scope is selected.
  2. Set indeterminate when at least one, but not every, item is selected.
  3. Update the same selected-ID state from the select-all and row checkbox handlers.
  4. Define whether the scope means the visible page, the current query, or every resource.

Don't store selection only in the checkbox element. Bulk-action payloads, selected counts, and post-action cleanup must use the same app state.


  • Toggle the checkbox with pointer, keyboard, and label activation.
  • Verify controlled and initial values don't drift after rerenders or form resets.
  • Submit checked and unchecked states and confirm the backend treats the missing unchecked value as intended.
  • Trigger, announce, and clear validation errors.
  • Test checked, unchecked, and indeterminate selection calculations.

Anchor to Remove Polaris ReactRemove Polaris React

After every call site is migrated, remove the Checkbox import and adapters that convert (checked, id) callbacks. Remove @shopify/polaris only after no other route in scope imports it.



Was this page helpful?