---
title: Migrate to the Polaris modal component
description: >-
  Learn how to migrate the Modal component to Polaris web components in checkout
  and customer account UI extensions.
source_url:
  html: 'https://shopify.dev/docs/apps/build/checkout/migrate-to-web-components/modal'
  md: >-
    https://shopify.dev/docs/apps/build/checkout/migrate-to-web-components/modal.md
---

# Migrate to the Polaris modal component

The Polaris modal component displays content in an overlay dialog that requires customer interaction. It replaces the previous [`Modal`](https://shopify.dev/docs/api/checkout-ui-extensions/2025-07/ui-components/overlays/modal) component and is available as [`<s-modal>`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal) in API versions 2025-10 and newer.

***

## Updated properties

The following properties are different in the Polaris modal component.

### title

The previous `Modal` `title` prop is now called [`heading`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#properties-propertydetail-heading).

### padding

The [`padding`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#properties-propertydetail-padding) prop changed from a boolean to an enum.

| Previous value | New value | Migration notes |
| - | - | - |
| `true` | `'base'` | `'base'` is the default. |
| `false` | `'none'` | Use `padding="none"` to remove padding. |

## Migrating padding values

##### Latest (Preact)

```tsx
import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
  render(<Extension />, document.body);
}

function Extension() {
  return (
    <>
      <s-button command="--show" commandFor="settings-modal" variant="primary">
        Open settings
      </s-button>
      <s-modal id="settings-modal" heading="Settings" padding="none">
        <s-paragraph>Content of the modal</s-paragraph>
      </s-modal>
    </>
  );
}
```

##### Pre-Polaris (2025-07)

```tsx
import {
  reactExtension,
  Button,
  Modal,
  TextBlock,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
  'purchase.checkout.block.render',
  () => <Extension />,
);

function Extension() {
  return (
    <Button
      overlay={
        <Modal id="settings-modal" title="Settings" padding={false}>
          <TextBlock>Content of the modal</TextBlock>
        </Modal>
      }
    >
      Open settings
    </Button>
  );
}
```

### size

The [`size`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#properties-propertydetail-size) prop values have changed.

| Previous value | New value | Migration notes |
| - | - | - |
| `'small'` | `'small'` or `'small-100'` | `'small'` is an alias for `'small-100'`. |
| `'auto'` | `'base'` | `'base'` is the default. |
| `'large'` | `'large'` or `'large-100'` | `'large'` is an alias for `'large-100'`. |
| `'max'` | `'max'` | No change needed. |

For more on the scale system, see [Scale](https://shopify.dev/docs/api/checkout-ui-extensions/latest/using-polaris-components#scale).

### primary​Action and secondary​Actions

The previous `Modal` `primaryAction` and `secondaryActions` props have been replaced with the [`primary-action`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#slots-propertydetail-primaryaction) and [`secondary-actions`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#slots-propertydetail-secondaryactions) slots.

| Previous prop | New pattern | Migration notes |
| - | - | - |
| `primaryAction` | `slot="primary-action"` | Place button children with `slot="primary-action"`. |
| `secondaryActions` | `slot="secondary-actions"` | Place button children with `slot="secondary-actions"`. |

## Migrating action props to slots

##### Latest (Preact)

```tsx
import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
  render(<Extension />, document.body);
}

function Extension() {
  return (
    <>
      <s-button command="--show" commandFor="confirm-modal">
        Delete item
      </s-button>
      <s-modal id="confirm-modal" heading="Confirm deletion">
        <s-paragraph>Are you sure you want to delete this item?</s-paragraph>
        <s-button slot="primary-action" command="--hide" commandFor="confirm-modal">
          Confirm
        </s-button>
        <s-button slot="secondary-actions" command="--hide" commandFor="confirm-modal">
          Cancel
        </s-button>
      </s-modal>
    </>
  );
}
```

##### Pre-Polaris (2025-07)

```tsx
import {
  reactExtension,
  Button,
  Modal,
  TextBlock,
  useApi,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
  'purchase.checkout.block.render',
  () => <Extension />,
);

function Extension() {
  const {ui} = useApi();

  return (
    <Button
      overlay={
        <Modal
          id="confirm-modal"
          title="Confirm deletion"
          primaryAction={
            <Button onPress={() => ui.overlay.close('confirm-modal')}>
              Confirm
            </Button>
          }
          secondaryActions={
            <Button onPress={() => ui.overlay.close('confirm-modal')}>
              Cancel
            </Button>
          }
        >
          <TextBlock>Are you sure you want to delete this item?</TextBlock>
        </Modal>
      }
    >
      Delete item
    </Button>
  );
}
```

### on​Close and on​Open

The previous `onClose` prop is now called [`onHide`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#events-propertydetail-hide). The previous `onOpen` prop is now called [`onShow`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#events-propertydetail-show).

| Previous prop | New prop |
| - | - |
| `onClose` | `onHide` |
| `onOpen` | `onShow` |

## Migrating onClose and onOpen

##### Latest (Preact)

```tsx
import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
  render(<Extension />, document.body);
}

function Extension() {
  return (
    <>
      <s-button command="--show" commandFor="welcome-modal">
        Open welcome
      </s-button>
      <s-modal
        id="welcome-modal"
        heading="Welcome"
        onShow={() => console.log('Modal opened')}
        onHide={() => console.log('Modal closed')}
      >
        <s-paragraph>Welcome to our store!</s-paragraph>
      </s-modal>
    </>
  );
}
```

##### Pre-Polaris (2025-07)

```tsx
import {
  reactExtension,
  Modal,
  TextBlock,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
  'purchase.checkout.block.render',
  () => <Extension />,
);

function Extension() {
  return (
    <Modal
      id="welcome-modal"
      title="Welcome"
      onOpen={() => console.log('Modal opened')}
      onClose={() => console.log('Modal closed')}
    >
      <TextBlock>Welcome to our store!</TextBlock>
    </Modal>
  );
}
```

***

## Removed properties

### overlay

The previous pattern of using the `overlay` prop on `Pressable` or `Button` to render a modal inline is no longer supported. Instead, render the modal as a sibling element and use [`command`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/actions/button#properties-propertydetail-command) with [`commandFor`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/actions/button#properties-propertydetail-commandfor) to open it.

**Tip:**

The `command` prop defaults to `--auto`, which resolves to `--toggle` for modals. You can omit `command` when targeting a modal and only set `commandFor`.

## Migrating overlay to command

##### Latest (Preact)

```tsx
import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
  render(<Extension />, document.body);
}

function Extension() {
  return (
    <>
      <s-button command="--show" commandFor="details-modal">
        More info
      </s-button>
      <s-modal id="details-modal" heading="Details">
        <s-paragraph>Delivery in 2 to 4 business days.</s-paragraph>
      </s-modal>
    </>
  );
}
```

##### Pre-Polaris (2025-07)

```tsx
import {
  reactExtension,
  Button,
  Modal,
  TextBlock,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
  'purchase.checkout.block.render',
  () => <Extension />,
);

function Extension() {
  return (
    <Button
      overlay={
        <Modal id="details-modal" title="Details">
          <TextBlock>Delivery in 2 to 4 business days.</TextBlock>
        </Modal>
      }
    >
      More info
    </Button>
  );
}
```

***

## New properties

The Polaris modal component introduces the following new properties:

| New prop | Type | Description |
| - | - | - |
| [`onAfterShow`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#events-propertydetail-aftershow) | event | Fires after the modal has fully opened and animations have completed. |
| [`onAfterHide`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#events-propertydetail-afterhide) | event | Fires after the modal has fully closed and animations have completed. |
| [`size`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#properties-propertydetail-size) values `'small-100'`, `'large-100'` | — | New size options on the [scale](https://shopify.dev/docs/api/checkout-ui-extensions/latest/using-polaris-components#scale). `'small'` and `'large'` remain available as aliases. |

***

## New methods

The Polaris modal component introduces a [`hideOverlay()`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/web-components/overlays/modal#methods-propertydetail-hideoverlay) method for programmatically closing the modal. This replaces the previous pattern of using `useApi()` and `ui.overlay.close()` to close overlays.

## Using hideOverlay to close the modal

##### Latest (Preact)

```tsx
import '@shopify/ui-extensions/preact';
import {render} from 'preact';
import {useRef} from 'preact/hooks';

export default function extension() {
  render(<Extension />, document.body);
}

function Extension() {
  const modalRef = useRef(null);

  function handleConfirm() {
    // Process action, then close the modal
    modalRef.current?.hideOverlay();
  }

  return (
    <>
      <s-button command="--show" commandFor="action-modal">
        Take action
      </s-button>
      <s-modal id="action-modal" heading="Confirm action" ref={modalRef}>
        <s-paragraph>Are you sure?</s-paragraph>
        <s-button slot="primary-action" onClick={handleConfirm}>
          Confirm
        </s-button>
      </s-modal>
    </>
  );
}
```

##### Pre-Polaris (2025-07)

```tsx
import {
  reactExtension,
  Button,
  Modal,
  TextBlock,
  useApi,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
  'purchase.checkout.block.render',
  () => <Extension />,
);

function Extension() {
  const {ui} = useApi();

  function handleConfirm() {
    // Process action, then close the modal
    ui.overlay.close('action-modal');
  }

  return (
    <Modal
      id="action-modal"
      title="Confirm action"
      primaryAction={
        <Button onPress={handleConfirm}>Confirm</Button>
      }
    >
      <TextBlock>Are you sure?</TextBlock>
    </Modal>
  );
}
```

***
