Skip to main content

Migrate Backdrop from Polaris React

Polaris web components don't provide a standalone backdrop. Replace the complete overlay composition that owns the Polaris React Backdrop from @shopify/polaris with <s-modal> or <s-popover>.

The overlay component owns its backdrop or outside-click area, stacking, scroll behavior, keyboard dismissal, and focus management. Don't recreate those pieces with a fixed-position element.


Anchor to Choose the owning overlayChoose the owning overlay

Existing usePolaris web componentsMigration notes
Dialog, confirmation, or focused forms-modalProvides a modal backdrop and traps focus while open.
Contextual content anchored to a buttons-popoverDismisses when the merchant interacts outside it.
Contextual actions onlys-menuUse instead of composing a backdrop with a custom action list.

If Backdrop only covered the page during loading, remove it and use local loading states on the affected controls or content. Don't block the entire embedded app with an empty overlay.


Anchor to Replace the complete compositionReplace the complete composition

The following migration replaces a custom dialog and its Backdrop with s-modal. The trigger and close button target the modal by id; clicking the managed backdrop or pressing Escape also dismisses it.

Migrating Backdrop to an s-modal composition

interface CustomerDetailsProps {
onClose(): void;
}

export function CustomerDetails({onClose}: CustomerDetailsProps) {
return (
<>
<s-button commandFor="customer-details">View customer</s-button>

<s-modal
id="customer-details"
heading="Maria Rodriguez"
onHide={onClose}
>
<s-text>Customer since April 2024</s-text>
<s-button
slot="secondary-actions"
commandFor="customer-details"
command="--hide"
>
Close
</s-button>
</s-modal>
</>
);
}
import {Backdrop, Button} from '@shopify/polaris';

interface CustomerDetailsProps {
open: boolean;
onOpen(): void;
onClose(): void;
}

export function CustomerDetails({
open,
onOpen,
onClose,
}: CustomerDetailsProps) {
return (
<>
<Button onClick={onOpen}>View customer</Button>
{open && (
<>
<Backdrop onClick={onClose} />
<div
role="dialog"
aria-modal="true"
aria-labelledby="customer-details-heading"
>
<h2 id="customer-details-heading">Maria Rodriguez</h2>
<p>Customer since April 2024</p>
<button onClick={onClose}>Close</button>
</div>
</>
)}
</>
);
}

Preview


All Polaris React Backdrop properties are removed with the component.

There is no replacement property. s-modal, s-popover, and s-menu own their position in the Shopify admin's overlay stack. Remove app-defined z-index rules and don't try to place an overlay below the admin navigation from inside the iframe.

There is no replacement property for changing the managed backdrop. Choose the overlay from the interaction instead:

  • Use s-modal when the rest of the interface must become inactive.
  • Use s-popover or s-menu for anchored content that dismisses on outside interaction without a modal backdrop.
  • Keep content on the page when it doesn't need overlay behavior.

Don't move backdrop-click logic to a custom page layer. Use the overlay's hide event for logic that must run on every dismissal. In React, use onHide={handleClose}. This covers backdrop or outside clicks, Escape, and controls that use command="--hide".

The event doesn't distinguish a backdrop click from another dismissal. If an action must happen only after explicit confirmation, put it on a labeled button in the modal instead of running it when the backdrop is clicked.

There is no direct replacement. Pointer, touch, and keyboard dismissal are built into the owning overlay. Move business logic from onTouchStart to an explicit action, or to onHide when it applies to every close path.

Remove setClosing and any associated closing state. It coordinated the Polaris React backdrop's mouse-down and click sequence; Polaris web components manage their own dismissal and animation. Use afterhide when cleanup must wait until the overlay finishes hiding.


  • Open the overlay from its real trigger and verify focus moves into it.
  • Dismiss it by backdrop or outside click, Escape, and every close button.
  • Confirm onHide logic runs once for each supported close path.
  • Verify focus returns to the trigger and the page doesn't remain scroll-locked.
  • Test long and scrollable content at narrow iframe sizes.
  • Remove custom portal, z-index, scroll-lock, and focus-trap code that only supported the old backdrop.
  • Remove the Polaris React Backdrop import after its final consumer is migrated.


Was this page helpful?