Beta Preview

Tabs

Tabs organize content into multiple sections and allow users to navigate between them.

Theme
orientation 
keyboardActivation 
isDisabled 
Example
Tabs.tsx
Tabs.css
import {Tabs, TabList, Tab, TabPanels, TabPanel} from './Tabs';
import {Form} from './Form';
import {TextField} from './TextField';
import {Button} from './Button';
import {RadioGroup, Radio} from './RadioGroup';
import {CheckboxGroup} from './CheckboxGroup';
import {Checkbox} from './Checkbox';

<Tabs>
  <TabList aria-label="Settings">
    <Tab id="general">General</Tab>
    <Tab id="appearance">Appearance</Tab>
    <Tab id="notifications">Notifications</Tab>
    <Tab id="profile">Profile</Tab>
  </TabList>
  <TabPanels>
    <TabPanel id="general">
      <Form>
        <TextField label="Homepage" defaultValue="react-aria.adobe.com" />
        <Checkbox defaultSelected>Show sidebar</Checkbox>
        <Checkbox>Show status bar</Checkbox>
      </Form>
    </TabPanel>
    <TabPanel id="appearance">
      <Form>
        <RadioGroup label="Theme" defaultValue="auto">
          <Radio value="auto">Auto</Radio>
          <Radio value="light">Light</Radio>
          <Radio value="dark">Dark</Radio>
        </RadioGroup>
        <RadioGroup label="Font size" defaultValue="medium">
          <Radio value="small">Small</Radio>
          <Radio value="medium">Medium</Radio>
          <Radio value="large">Large</Radio>
        </RadioGroup>
      </Form>
    </TabPanel>
    <TabPanel id="notifications">
      <CheckboxGroup label="Notifications settings" defaultValue={['account', 'dms']}>
        <Checkbox value="account">Account activity</Checkbox>
        <Checkbox value="mentions">Mentions</Checkbox>
        <Checkbox value="dms">Direct message</Checkbox>
        <Checkbox value="marketing">Marketing emails</Checkbox>
      </CheckboxGroup>
    </TabPanel>
    <TabPanel id="profile">
      <Form>
        <TextField label="Name" defaultValue="Devon Govett" />
        <TextField label="Username" defaultValue="@devongovett" />
        <Button>Update profile</Button>
      </Form>
    </TabPanel>
  </TabPanels>
</Tabs>

Content

TabList follows the Collection Components API, accepting both static and dynamic collections. This example shows a dynamic collection, passing a list of objects to the items prop, and a function to render the children.

Tab body 1
import {Tabs, TabList, Tab, TabPanels, TabPanel} from './Tabs';
import {Button} from './Button';
import {useState} from 'react';

function Example() {
  let [tabs, setTabs] = useState([
    {id: 1, title: 'Tab 1', content: 'Tab body 1'},
    {id: 2, title: 'Tab 2', content: 'Tab body 2'},
    {id: 3, title: 'Tab 3', content: 'Tab body 3'}
  ]);

  let addTab = () => {
    setTabs(tabs => [
      ...tabs,
      {
        id: tabs.length + 1,
        title: `Tab ${tabs.length + 1}`,
        content: `Tab body ${tabs.length + 1}`
      }
    ]);
  };

  let removeTab = () => {
    if (tabs.length > 1) {
      setTabs(tabs => tabs.slice(0, -1));
    }
  };

  return (
    <Tabs style={{width: '100%'}}>
      <div style={{display: 'flex'}}>
        <TabList 
          aria-label="Dynamic tabs"
          items={tabs}
          style={{flex: 1}}>
          {item => <Tab>{item.title}</Tab>}
        </TabList>
        <div className="button-group">
          <Button onPress={addTab}>Add tab</Button>
          <Button onPress={removeTab}>Remove tab</Button>
        </div>
      </div>
      <TabPanels items={tabs}>
        {item => <TabPanel>{item.content}</TabPanel>}
      </TabPanels>
    </Tabs>
  );
}

Use the href prop on a <Tab> to create a link. See the client side routing guide to learn how to integrate with your framework. This example uses a simple hash-based router to sync the selected tab to the URL.

import {Tabs, TabList, Tab, TabPanels, TabPanel} from './Tabs';
import {useSyncExternalStore} from 'react';

export default function Example() {
  let hash = useSyncExternalStore(subscribe, getHash, getHashServer);

  return (
    <Tabs selectedKey={hash}>
      <TabList aria-label="Tabs">
        <Tab id="#/" href="#/">Home</Tab>
        <Tab id="#/shared" href="#/shared">Shared</Tab>
        <Tab id="#/deleted" href="#/deleted">Deleted</Tab>
      </TabList>
      <TabPanels>
        <TabPanel id="#/">Home</TabPanel>
        <TabPanel id="#/shared">Shared</TabPanel>
        <TabPanel id="#/deleted">Deleted</TabPanel>
      </TabPanels>
    </Tabs>
  );
}

function getHash() {
  return location.hash.startsWith('#/') ? location.hash : '#/';
}

function getHashServer() {
  return '#/';
}

function subscribe(fn) {
  addEventListener('hashchange', fn);
  return () => removeEventListener('hashchange', fn);
}

Selection

Use the defaultSelectedKey or selectedKey prop to set the selected tab. The selected key corresponds to the id prop of a <Tab>. Tabs can be disabled with the isDisabled prop. See the selection guide for more details.

Selected tab: files

import type {Key} from 'react-aria-components';
import {Tabs, TabList, Tab, TabPanels, TabPanel} from './Tabs';
import Home from '@react-spectrum/s2/illustrations/gradient/generic2/Home';
import Folder from '@react-spectrum/s2/illustrations/gradient/generic2/FolderOpen';
import Search from '@react-spectrum/s2/illustrations/gradient/generic2/Search';
import Settings from '@react-spectrum/s2/illustrations/gradient/generic1/GearSetting';
import {useState} from 'react';

function Example() {
  let [tab, setTab] = useState<Key>("files");

  return (
    <div>
      <Tabs
        selectedKey={tab}
        onSelectionChange={setTab}
      >
        <TabList aria-label="Tabs">
          <Tab id="home">Home</Tab>
          <Tab id="files">Files</Tab>
          <Tab id="search" isDisabled>Search</Tab>
          <Tab id="settings">Settings</Tab>
        </TabList>
        <TabPanels>
          <TabPanel id="home" style={{display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
            <Home />
          </TabPanel>
          <TabPanel id="files" style={{display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
            <Folder />
          </TabPanel>
          <TabPanel id="search" style={{display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
            <Search />
          </TabPanel>
          <TabPanel id="settings" style={{display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
            <Settings />
          </TabPanel>
        </TabPanels>
      </Tabs>
      <p>Selected tab: {tab}</p>
    </div>
  );
}

API

Shows a tabs component with labels for each tab, a selection state, and the tab panel.Section 1Section 2TabTab (selected)Tab listTab panel