Migrate Navigation from Polaris React
Replace Polaris React Navigation with s-app-nav. App navigation renders in the Shopify admin rather than inside the app's Frame.
s-app-nav supports one level of s-link children. Flatten sections and nested items intentionally instead of reproducing the old navigation hierarchy.
Migrating primary app navigation
Polaris web components
function AppNavigation() {
return (
<s-app-nav>
<s-link href="/products">Products</s-link>
<s-link href="/settings">Settings</s-link>
</s-app-nav>
);
}Polaris React
import {Navigation} from '@shopify/polaris';
export function AppNavigation() {
return (
<Navigation location="/products">
<Navigation.Section
items={[
{label: 'Home', url: '/'},
{label: 'Products', url: '/products'},
{label: 'Settings', url: '/settings'},
]}
/>
</Navigation>
);
}s-app-nav renders only in the Shopify admin shell, so this admin navigation example isn't available as an isolated live preview.
Keep s-app-nav mounted near the app root rather than rendering a different navigation component for each route.
| Polaris React | Polaris web components | Migration notes |
|---|---|---|
Navigation.Section | Remove | s-app-nav doesn't display section containers or headings. |
items | s-link children | Render each primary destination as a link. |
Item label | Link text | Use a concise noun that matches the destination page title. |
Item url | href | Preserve a real URL for direct and modified navigation. |
location and item selected | Remove | The host derives the active destination from the current URL. |
Item onClick | Link navigation or a page-level action | Keep navigation declarative. Move non-navigation actions out of app nav. |
Item icon, badge, exactMatch, or matchPaths | Remove | The destination doesn't expose these presentation and matching controls. |
s-app-nav doesn't support nested items. For each subNavigationItems tree, choose one of these migrations:
- Promote frequently used destinations to top-level app-nav links.
- Keep one top-level link and move child destinations into the landing page.
- Use page-level tabs or links when the destinations are alternate views of one resource.
- Remove destinations that duplicate breadcrumbs, page actions, or contextual resource links.
Don't encode hierarchy into labels such as Products > Collections. Use concise labels and make the destination page establish its own context.
Anchor to Preserve router behaviorPreserve router behavior
Use href values that the embedded app router can resolve directly. Verify direct loads, browser back and forward navigation, copied URLs, and modified clicks. Don't replace links with click handlers that call the router unless the control isn't navigation.
If the app mounts below a path such as /app, then include that base path consistently in every destination. The App nav reference documents rel="home" specifically for links inside s-app-nav:
The rel="home" link sets the non-root home route for the app name in Shopify admin; it doesn't render a visible Home navigation item. Generic s-link JSX types don't expose rel, so keep this App nav-specific pattern in markup rather than reusing it on links elsewhere.
Anchor to Remove the Frame dependencyRemove the Frame dependency
Polaris React passed Navigation to the navigation prop on Frame. Render s-app-nav near the app root, and remove Frame state used only to open or dismiss mobile navigation. The Shopify admin owns the surrounding responsive shell.
Anchor to Test the migrationTest the migration
- Visit every destination through app nav and by loading its URL directly.
- Verify the active destination follows route changes and browser history.
- Test modified clicks, opening links in a new tab, and copied URLs.
- Confirm that nested destinations remain discoverable after flattening.
- Resize the app, and confirm that app nav doesn't duplicate an in-iframe navigation shell.
Anchor to Remove Polaris ReactRemove Polaris React
After every Navigation call site is migrated, remove Navigation, its Frame prop, mobile-navigation state, and imported navigation icons used only by it. Remove @shopify/polaris only after no other route in scope imports it.