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';
import {Plus, Minus} from 'lucide-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} aria-label="Add tab">
            <Plus />
          </Button>
          <Button onPress={removeTab} aria-label="Remove tab">
            <Minus />
          </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 framework setup 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>
  );
}

Examples

API

Shows a tabs component with labels for each tab, a selection state, and the tab panel.Section 1Section 2TabTab (selected)Tab listTab panel
<Tabs>
  <TabList>
    <Tab>
      <SelectionIndicator />
    </Tab>
  </TabList>
  <TabPanels>
    <TabPanel />
  </TabPanels>
</Tabs>

Tabs

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

NameTypeDefault
isDisabledbooleanDefault:
Whether the TabList is disabled. Shows that a selection exists, but is not available in that circumstance.
keyboardActivation'automatic''manual'Default: 'automatic'
Whether tabs are activated automatically on focus or manually.
orientationDefault: 'horizontal'
The orientation of the tabs.
children<>Default:
The children of the component. A function may be provided to alter the children based on component state.
selectedKeyKeynullDefault:
The currently selected key in the collection (controlled).
defaultSelectedKeyKeyDefault:
The initial selected key in the collection (uncontrolled).
onSelectionChange(key: Key) => voidDefault:
Handler that is called when the selection changes.
disabledKeysIterable<Key>Default:
The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.

Default className: react-aria-Tabs

Render PropCSS Selector
orientationCSS Selector: [data-orientation="horizontal | vertical"]
The orientation of the tabs.

TabList

A TabList is used within Tabs to group tabs that a user can switch between. The ids of the items within the <TabList> must match up with a corresponding item inside the <TabPanels>.

NameType
childrenReactNode(item: T) => ReactNode
The contents of the collection.
itemsIterable<T>
Item objects in the collection.
dependenciesReadonlyArray<any>
Values that should invalidate the item cache when using dynamic collections.

Default className: react-aria-TabList

Render PropCSS Selector
orientationCSS Selector: [data-orientation="horizontal | vertical"]
The orientation of the tab list.
stateCSS Selector:
State of the tab list.

Tab

A Tab provides a title for an individual item within a TabList.

NameType
idKey
The unique id of the tab.
isDisabledboolean
Whether the tab is disabled.
children<>
The children of the component. A function may be provided to alter the children based on component state.

Default className: react-aria-Tab

Render PropCSS Selector
isHoveredCSS Selector: [data-hovered]
Whether the tab is currently hovered with a mouse.
isPressedCSS Selector: [data-pressed]
Whether the tab is currently in a pressed state.
isSelectedCSS Selector: [data-selected]
Whether the tab is currently selected.
isFocusedCSS Selector: [data-focused]
Whether the tab is currently focused.
isFocusVisibleCSS Selector: [data-focus-visible]
Whether the tab is currently keyboard focused.
isDisabledCSS Selector: [data-disabled]
Whether the tab is disabled.

TabPanels

Groups multiple <TabPanel> elements, and provides CSS variables for animated transitions.

NameType
childrenReactNode(item: object) => ReactNode
The contents of the collection.
itemsIterable<object>
Item objects in the collection.
dependenciesReadonlyArray<any>
Values that should invalidate the item cache when using dynamic collections.
disabledKeysIterable<Key>
The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.

Default className: react-aria-TabPanels

CSS Variable
--tab-panel-width
The width of the active tab panel in pixels. Useful for animations.
--tab-panel-height
The height of the active tab panel in pixels. Useful for animations.

TabPanel

A TabPanel provides the content for a tab.

NameTypeDefault
shouldForceMountbooleanDefault: false
Whether to mount the tab panel in the DOM even when it is not currently selected. Inactive tab panels are inert and cannot be interacted with. They must be styled appropriately so this is clear to the user visually.
idKeyDefault:
The unique id of the tab.
children<>Default:
The children of the component. A function may be provided to alter the children based on component state.

Default className: react-aria-TabPanel

Render PropCSS Selector
isFocusedCSS Selector: [data-focused]
Whether the tab panel is currently focused.
isFocusVisibleCSS Selector: [data-focus-visible]
Whether the tab panel is currently keyboard focused.
isInertCSS Selector: [data-inert]
Whether the tab panel is currently non-interactive. This occurs when the shouldForceMount prop is true, and the corresponding tab is not selected.
isEnteringCSS Selector: [data-entering]
Whether the tab panel is currently entering. Use this to apply animations.
isExitingCSS Selector: [data-exiting]
Whether the tab panel is currently exiting. Use this to apply animations.
stateCSS Selector:
State of the tab list.