The API for the React Spectrum Tabs
component changed significantly between Beta and RC. The documentation on the React Spectrum website has been updated to follow the new API. This guide aims to assist developers using the beta and wishing to upgrade to the new RC version.
In the previous API, the Tabs
component accepted <Item>
elements as children. Each item had a title
prop representing the tab header, and children
representing the tab contents.
<Tabs aria-label="History of Ancient Rome">
<Item title="Founding of Rome" key="FoR">
<Content marginTop="size-250" marginStart="size-125">
<Text>Arma virumque cano, Troiae qui primus ab oris.</Text>
</Content>
</Item>
<Item title="Monarchy and Republic" key="MaR">
<Content marginTop="size-250" marginStart="size-125">
<Text>Senatus Populusque Romanus.</Text>
</Content>
</Item>
<Item title="Empire" key="Emp">
<Content marginTop="size-250" marginStart="size-125">
<Text>Alea jacta est.</Text>
</Content>
</Item>
</Tabs>
The new API allows for additional layout flexibility, along with support for more complex tab headers, e.g. including icons. This is accomplished by separating the tab headers and tab contents into separate containers: <TabList>
and <TabPanels>
. These elements each contain <Item>
elements with corresponding keys. This allows you to place the tab list and tab panels anywhere you like inside the <Tabs>
container, and even add additional elements between them or wrapping them.
Here is the above example in the new API:
<Tabs aria-label="History of Ancient Rome">
<TabList>
<Item key="FoR">Founding of Rome</Item>
<Item key="MaR">Monarchy and Republic</Item>
<Item key="Emp">Empire</Item>
</TabList>
<TabPanels>
<Item key="FoR">
Arma virumque cano, Troiae qui primus ab oris.
</Item>
<Item key="MaR">
Senatus Populusque Romanus.
</Item>
<Item key="Emp">
Alea jacta est.
</Item>
</TabPanels>
</Tabs>
As you can see, the items in the tab list and tab panels have matching keys so that when a user selects an item in the tab list, the corresponding tab panel is shown.
To demonstrate the kind of layout flexibility this allows, here’s an example. It shows a dynamic list of tabs, and the user can press the button next to the tab list to add additional tabs.
let [tabs, setTabs] = useState([
{name: 'Tab 1', contents: 'Tab 1 contents'},
{name: 'Tab 2', contents: 'Tab 2 contents'}
]);
let addTab = () => {
setTabs(tabs => [
...tabs,
{name: `Tab ${tabs.length + 1}`, contents: `Tab ${tabs.length + 1} contents`}
]);
};
<Tabs items={tabs}>
<Flex direction="row" alignItems="center">
<TabList>
{tab => <Item>{tab.name}</Item>
</TabList>
<ActionButton onPress={addTab}>Add tab</ActionButton>
</Flex>
<TabPanels>
{tab => <Item>{tab.contents}</Item>
</TabPanels>
</Tabs>
This API also allows for rich tab headers, including more than just text (e.g. icons). This can be done by adding the icon component inside the Item
, along with a <Text>
element to contain the tab title.
<Tabs aria-label="History of Ancient Rome">
<TabList>
<Item key="dashboard">
<DashboardIcon />
<Text>Dashboard</Text>
</Item>
<Item key="calendar">
<CalendarIcon />
<Text>Calendar</Text>
</Item>
</TabList>
<TabPanels>
<Item key="dashboard">
Dashboard contents.
</Item>
<Item key="MaR">
Calendar contents.
</Item>
</TabPanels>
</Tabs>