Skip to main content

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 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.


Anchor to Map the tab behaviorMap the tab behavior

Polaris ReactPolaris web componentsMigration notes
tabsButtons in an s-button-groupKeep a stable ID for each view and use gap="none" for the documented segmented appearance.
selectedApp-owned view stateUse the same value to derive the active content, visible check marker, and aria-pressed.
onSelectButton onClick handlersUpdate the stable view ID rather than an array index.
URL-backed tabsButtons with href or linksPreserve browser history and mark the current destination with aria-current="page".
ARIA tab panelsNo direct equivalentPreserve Polaris React until the complete tablist, tab, and tabpanel interaction can be implemented and reviewed.

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

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>
);
}
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}
/>
);
}

Preview


Was this page helpful?