---
title: Migrate Page from Polaris React
description: >-
  Replace Polaris React Page with s-page and migrate headings, breadcrumbs,
  actions, metadata, width, and page content into supported properties and
  slots.
source_url:
  html: 'https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/page'
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/page.md
api_name: app-home
---

# Migrate Page from Polaris React

Replace Polaris React `Page` with [`s-page`](https://shopify.dev/docs/api/app-home/web-components/layout-and-structure/page). Migrate the page shell as one unit so the heading, hierarchy, actions, width, and route content remain coherent in the Shopify admin title bar.

***

## Migrate a page shell

The destination uses supported page slots for breadcrumbs and actions. Omit content that doesn't have a title-bar slot, such as `titleMetadata`, from the page shell. Reintroduce it in page content only when it provides necessary information there.

## Migrating a products page

##### Polaris web components

```tsx
export function ProductsPage({
  onCreateProduct,
  onDuplicateProduct,
  onArchiveProduct,
}) {
  return (
    <s-page heading="Products">
      <s-link slot="breadcrumb-actions" href="/app">
        Home
      </s-link>
      <s-button
        slot="primary-action"
        variant="primary"
        onClick={onCreateProduct}
      >
        Create product
      </s-button>
      <s-button slot="secondary-actions" onClick={onDuplicateProduct}>
        Duplicate product
      </s-button>
      <s-button slot="secondary-actions" onClick={onArchiveProduct}>
        Archive product
      </s-button>
      <s-section heading="All products">
        <s-stack gap="small">
          <s-paragraph>
            Manage the products available through your app.
          </s-paragraph>
        </s-stack>
      </s-section>
    </s-page>
  );
}
```

##### Polaris React

```tsx
import {Badge, Card, Page, Text} from '@shopify/polaris';

export function ProductsPage({onCreate, onDuplicate, onArchive}) {
  return (
    <Page
      title="Products"
      backAction={{content: 'Home', url: '/app'}}
      titleMetadata={<Badge tone="success">Active</Badge>}
      primaryAction={{content: 'Create product', onAction: onCreate}}
      secondaryActions={[
        {content: 'Duplicate product', onAction: onDuplicate},
        {content: 'Archive product', onAction: onArchive},
      ]}
    >
      <Card>
        <Text as="h2" variant="headingSm">All products</Text>
        <Text as="p">Manage the products available through your app.</Text>
      </Card>
    </Page>
  );
}
```

***

## Map the page header

| Polaris React prop | Replacement | How to migrate |
| - | - | - |
| `title` | `heading` | Keep one concise heading that identifies the route or resource. |
| `backAction` | `s-link` | Set `slot="breadcrumb-actions"`, move `url` to `href`, and use the parent page name as visible text. |
| `primaryAction` | `s-button` | Set `slot="primary-action"` and `variant="primary"`. Reconnect loading, disabled, and click behavior. |
| `secondaryActions` | `s-button` or `s-button-group` | Put frequent actions in `slot="secondary-actions"`. Use one slotted trigger and an unslotted `s-menu` for overflow actions. |
| `actionGroups` | `s-button` and `s-menu` | Add a slotted trigger for each necessary group. Keep its menu unslotted, and remove groups that duplicate page content or navigation. |
| `titleMetadata` | Omit from the page shell | `s-page` doesn't have an accessory slot. Add status to page content only when it provides necessary information there. |
| `subtitle` and `additionalMetadata` | Introductory content in the page body | Use semantic paragraphs, badges, or links in the relevant section. |
| `pagination` | `s-table` pagination or an in-content pagination composition | Keep pagination with the data it controls rather than in the title bar. |
| `titleHidden` | A visible `heading` in most routes | Don't remove the page's only heading. If the design genuinely needs no title bar, preserve an accessible content heading. |
| `pageReadyAccessibilityLabel` | Route-level announcement and focus behavior | Announce the loaded route through the router or loading pattern instead of passing a page-only compatibility prop. |

The `breadcrumb-actions` slot accepts links. The `primary-action` slot accepts one primary button. The `secondary-actions` slot accepts buttons or a button group with secondary or automatic variants.

***

## Map page width and regions

| Polaris React | Polaris web components | Migration notes |
| - | - | - |
| Default width | `inlineSize="base"` or omit the property | Use for most pages. |
| `narrowWidth` | `inlineSize="small"` | Use for focused, single-column tasks. |
| `fullWidth` | `inlineSize="large"` | Use only when the content benefits from the available width, such as a dense data view. |
| Layout sidebar | `slot="aside"` | The aside renders only with `inlineSize="base"`. Keep primary task content in the default slot. |
| `children` | Default page content | Compose sections, stacks, grids, and documented patterns from the information hierarchy. |

Don't replace every Polaris React `Card` with another visual wrapper. Use `s-section` for a standard page region and a documented pattern when the old card represented a complete task.

***

## Keep route behavior intact

Keep `s-page` at the route level instead of wrapping reusable leaf components in their own page. Update its heading and actions from the active route, and remove stale actions when navigating. Verify direct loads, browser back and forward navigation, and focus after the route finishes loading.

If the page contains unsaved form state, migrate its save bar and navigation protection before removing the Polaris React page shell.

***

## Test the migration

* Load the route directly and through app navigation, then verify the heading and breadcrumbs.
* Run primary, secondary, overflow, disabled, loading, success, and failure action paths.
* Test small, base, and large widths only where the route uses them.
* Verify the aside doesn't contain primary task controls and behaves correctly at narrow widths.
* Confirm browser history and route changes don't leave stale title-bar actions.
* Verify heading order, link behavior, action names, and keyboard focus.

***

## Remove Polaris React

After every call site is migrated, remove `Page`, action descriptor builders, and CSS that targets Polaris page internals. Remove `@shopify/polaris` only after no other route in scope imports it.

***

## Related guidance

* [Page component](https://shopify.dev/docs/api/app-home/web-components/layout-and-structure/page)
* [Migrate Breadcrumbs from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/breadcrumbs)
* [Migrate PageActions from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/page-actions)
* [Migrate Layout from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/layout)

***
