Skip to content

Instantly share code, notes, and snippets.

@devongovett
Created May 1, 2021 00:06
Show Gist options
  • Save devongovett/86f49cf669b4178d03fcd73c50a2cc4a to your computer and use it in GitHub Desktop.
Save devongovett/86f49cf669b4178d03fcd73c50a2cc4a to your computer and use it in GitHub Desktop.
Migration guide for React Spectrum Tabs

Tabs API Migration Guide

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment