---
title: Migrate Button from Polaris React
description: >-
  Replace Polaris React Button with s-button while preserving action,
  navigation, submission, loading, tone, and accessible naming.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/button
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/button.md
api_name: app-home
---

# Migrate Button from Polaris React

Replace Polaris React `Button` with [`s-button`](https://shopify.dev/docs/api/app-home/web-components/actions/button). Decide first whether the call site performs an action, submits a form, navigates, downloads a file, or opens another component.

***

## Migrate a primary action

This example preserves the button's hierarchy, label, and click behavior. Replace the imported Polaris React icon component with the documented Polaris web-component icon name.

## Migrating a primary action with an icon

##### Polaris web components

```tsx
export function AddProductButton({onAddProduct}) {
  return (
    <s-button variant="primary" icon="plus" onClick={onAddProduct}>
      Add product
    </s-button>
  );
}
```

##### Polaris React

```tsx
import {Button} from '@shopify/polaris';
import {PlusIcon} from '@shopify/polaris-icons';

export function AddProductButton({onAddProduct}) {
  return (
    <Button variant="primary" icon={PlusIcon} onClick={onAddProduct}>
      Add product
    </Button>
  );
}
```

***

## Replace Button properties

| Polaris React | Polaris web components | Migration notes |
| - | - | - |
| `onClick` | `onClick` | Guard duplicate async requests in app logic. |
| `submit` | `type="submit"` | Keep the button inside its native form. |
| `url` | `href` | Use `s-link` for inline navigation; use button styling only when the hierarchy calls for it. |
| `external` and `target` | `target`, commonly `_blank` | Preserve a real `href`; don't emulate navigation in `onClick`. |
| `download` | `download` with `href` | Test the actual response and filename. |
| `loading` | `loading` | Keep it true for the complete request and prevent repeat submission in app state. |
| `disabled` | `disabled` | Explain unavailable prerequisites visibly; disabled controls aren't focusable. |
| `variant="primary"` | `variant="primary"` | Keep one clear primary action for the current scope. |
| Plain or low-emphasis variants | `variant="tertiary"` | Re-evaluate hierarchy instead of copying appearance names. |
| `tone="critical"` or `destructive` | `tone="critical"` | Use for destructive actions that are difficult to reverse. |
| `icon` | A documented icon name string in `icon` | Remove Polaris React icon imports. |
| `iconOnly` or no visible text | `icon` plus `accessibilityLabel` | Name the action, not the icon. |
| `disclosure` | A trigger with `commandFor` pointing to `s-menu` or `s-popover` | Let the destination overlay own open and close state. |
| `pressed` | Use the control that matches the state, such as `s-switch` or a choice field | Don't use an action button as a persistent setting. |
| `fullWidth` | `inlineSize="fill"` | Let the button fill the available inline size only when the containing layout calls for it. |
| `size`, `textAlign`, and `monochrome` | Containing layout and destination hierarchy | Remove appearance-only compatibility props. |

***

## Handle actions safely

Set `loading` before awaiting an operation, keep the label specific, and show success or failure feedback. Don't clear form values or navigate until persistence succeeds. For destructive changes, confirm scope in a modal and keep the critical action there.

Use `commandFor` when the button opens an `s-menu`, `s-popover`, `s-modal`, or another command target. Keep the target as a sibling with a stable `id`; don't retain React state that only opened the Polaris overlay.

***

## Test the migration

* Exercise enabled, disabled, loading, success, failure, and retry states.
* Submit with pointer, keyboard, and Enter from the form.
* Test internal links, external links, modified clicks, and downloads.
* Verify icon-only labels and focus order.
* Confirm destructive actions show the correct scope and can't run twice.

***

## Remove Polaris React

Remove `Button`, Polaris icon imports, disclosure state, and prop adapters after each call site is migrated. Remove `@shopify/polaris` only after no other route in scope imports it.

***

## Related guidance

* [Button component](https://shopify.dev/docs/api/app-home/web-components/actions/button)
* [Migrate ButtonGroup from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/button-group)
* [Migrate Link from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/link)

***
