---
title: Migrate Tabs from Polaris React
description: Understand the supported migration paths for the Polaris React Tabs component.
source_url:
  html: 'https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/tabs'
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/tabs.md
api_name: app-home
---

# Migrate Tabs from Polaris React

App Home has no direct tab web component. Choose a replacement based on what the old tabs do instead of recreating the `Tabs` API.

* For a small in-page view switcher, use a segmented [`s-button-group`](https://shopify.dev/docs/api/app-home/web-components/actions/button-group) with `gap="none"`. Drive the active button from app state, mark it visually, and expose `aria-pressed` on every option.
* When each view has its own addressable page, use buttons with `href` or real links and identify the current destination with `aria-current="page"`.
* When the interface requires tablist keyboard behavior, focus management, and associated tab panels, keep Polaris React during an incremental migration or implement and separately review the complete ARIA tabs pattern.

A visual marker alone isn't enough. The selected state, accessible state, content, and URL or app state must all agree.

***

## Map the tab behavior

| Polaris React | Polaris web components | Migration notes |
| - | - | - |
| `tabs` | Buttons in an `s-button-group` | Keep a stable ID for each view and use `gap="none"` for the documented segmented appearance. |
| `selected` | App-owned view state | Use the same value to derive the active content, visible check marker, and `aria-pressed`. |
| `onSelect` | Button `onClick` handlers | Update the stable view ID rather than an array index. |
| URL-backed tabs | Buttons with `href` or links | Preserve browser history and mark the current destination with `aria-current="page"`. |
| ARIA tab panels | No direct equivalent | Preserve Polaris React until the complete tablist, tab, and tabpanel interaction can be implemented and reviewed. |

***

## Migration example

The example replaces a two-option in-page view switcher with the segmented button-group pattern documented by App Home. The active button has a check icon and exposes `aria-pressed="true"`; changing the view must also replace the associated product results.

## Replacing tabs with a segmented view switcher

##### Polaris web components

```tsx
export function ProductViews({
  view,
  onViewChange,
}: {
  view: 'all' | 'active';
  onViewChange: (view: 'all' | 'active') => void;
}) {
  return (
    <s-button-group gap="none" accessibilityLabel="Product views">
      <s-button
        slot="secondary-actions"
        variant="secondary"
        icon={view === 'all' ? 'check' : undefined}
        aria-pressed={view === 'all'}
        onClick={() => onViewChange('all')}
      >
        All products
      </s-button>
      <s-button
        slot="secondary-actions"
        variant="secondary"
        icon={view === 'active' ? 'check' : undefined}
        aria-pressed={view === 'active'}
        onClick={() => onViewChange('active')}
      >
        Active products
      </s-button>
    </s-button-group>
  );
}
```

##### Polaris React

```tsx
import {useState} from 'react';
import {Tabs} from '@shopify/polaris';


export function TabsMigrationExample() {
  const [selectedTab, setSelectedTab] = useState(0);

  return (
    <Tabs
      tabs={[{id: 'all', content: 'All'}, {id: 'active', content: 'Active'}]}
      selected={selectedTab}
      onSelect={setSelectedTab}
    />
  );
}
```

***
