---
title: Migrate Popover from Polaris React
description: >-
  Replace the Polaris React Popover component and its open state with s-popover
  and command-based controls.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/popover
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/popover.md
api_name: app-home
---

# Migrate Popover from Polaris React

Replace Polaris React `Popover` with [`s-popover`](https://shopify.dev/docs/api/app-home/web-components/overlays/popover) for contextual content anchored to a trigger. Use [`s-menu`](https://shopify.dev/docs/api/app-home/web-components/actions/menu) when the overlay contains only actions, and use [`s-modal`](https://shopify.dev/docs/api/app-home/web-components/overlays/modal) when the task requires more space or focused attention.

***

## Migrate contextual content

Polaris React passes an activator into `Popover` and controls `active` state. Polaris web components render a trigger and popover as siblings. The trigger references the popover's ID with `commandFor`, so you can remove state used only to open and close the overlay.

## Migrating inventory details

##### Polaris web components

```tsx
function InventoryDetails() {
  return (
    <>
      <s-button commandFor="inventory-popover">View inventory</s-button>
      <s-popover id="inventory-popover" inlineSize="320px">
        <s-box padding="base">
          <s-stack gap="small">
            <s-heading>Inventory</s-heading>
            <s-text>Ottawa: 12 available</s-text>
            <s-text>Toronto: 8 available</s-text>
          </s-stack>
        </s-box>
      </s-popover>
    </>
  );
}
```

##### Polaris React

```tsx
import {Button, Popover, Text} from '@shopify/polaris';
import {useState} from 'react';

export function InventoryDetails() {
  const [active, setActive] = useState(false);

  return (
    <Popover
      active={active}
      activator={
        <Button onClick={() => setActive((open) => !open)}>
          View inventory
        </Button>
      }
      onClose={() => setActive(false)}
      sectioned
    >
      <Text as="h2" variant="headingSm">Inventory</Text>
      <Text as="p">Ottawa: 12 available</Text>
      <Text as="p">Toronto: 8 available</Text>
    </Popover>
  );
}
```

***

## Updated properties

| Polaris React | Polaris web components | Migration notes |
| - | - | - |
| `activator` | A sibling `s-button` with `commandFor` | Give the popover a stable, unique ID. Omit `command` to toggle, or use `command="--show"` when the trigger should only open it. |
| `active` | Remove | The trigger and popover manage visibility through commands. |
| `onClose` | The `hide` event; use `command="--hide"` on an explicit close or apply action | Keep a callback only when app state must react to every close path. |
| `sectioned` | `s-box padding="base"` or `s-section` | Compose the content and spacing explicitly. |
| `fullWidth` or `fluidContent` | No direct equivalent | Set `inlineSize` only when the content needs an explicit width. |
| `preferredAlignment` and `preferredPosition` | Remove | The popover positions itself from its trigger. |
| `Popover.Pane` and `Popover.Section` | `s-box`, `s-section`, or `s-stack` | Preserve content hierarchy and scrolling requirements through composition. |

`s-popover` can't open automatically on page load. A user interaction must open it. If content must be visible immediately, then place it in the page or use a different pattern.

The `command` value is optional. Omit it when the trigger should toggle the popover. Use explicit `--show` or `--hide` when a control has only one valid outcome.

***

## Choose the right overlay

* Use `s-popover` for contextual information, settings, or a compact form related to one trigger.
* Use `s-menu` for a compact list of actions. Menu children are buttons, not Polaris React action descriptor objects.
* Use `s-modal` for confirmation, complex forms, or tasks that need focused attention.
* Keep persistent actions and important instructions visible in the page.

Don't move essential instructions into a popover only to reduce page content. Users shouldn't need to discover hidden content to complete the task.

***

## Preserve app state intentionally

Remove `active` state when it exists only to control the overlay. Keep state for values inside the popover, applied filters, pending operations, or results.

When applying a form or filter, update app state first and then hide the popover. Keep validation errors visible and leave the popover open when the operation fails.

***

## Test the migration

* Open and close the popover with a pointer and keyboard.
* Verify the trigger keeps focus when the popover opens, and remains usable after it closes.
* Test the Escape key, outside interaction, and any explicit hide control that the app adds; `s-popover` doesn't add a dismiss control.
* Confirm that content remains usable at narrow app widths and with long translated text.
* Verify nested interactive content doesn't close before its action completes.

***

## Remove Polaris React

After every `Popover` call site is migrated, remove the `Popover` import and state or callbacks used only to control it. Remove `@shopify/polaris` only after no other route in scope imports it.

***

## Related guidance

* [Popover component](https://shopify.dev/docs/api/app-home/web-components/overlays/popover)
* [Menu component](https://shopify.dev/docs/api/app-home/web-components/actions/menu)
* [Modal component](https://shopify.dev/docs/api/app-home/web-components/overlays/modal)
* [Migrate ActionList from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/action-list)

***
