Tabs organize content into multiple sections and allow users to navigate between them.
Vanilla CSS theme
--tint CSS variable used by the Vanilla CSS examples.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.
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>
);
}
Links
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
<Tabs>
<TabList>
<Tab>
<SelectionIndicator />
</Tab>
</TabList>
<TabPanels>
<TabPanel />
</TabPanels>
</Tabs>
Tabs
Tabs organize content into multiple sections and allow users to navigate between them.
| Name | Type | Default |
|---|---|---|
isDisabled | boolean | Default: — |
| 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. | ||
orientation | Orientation | Default: 'horizontal'
|
| The orientation of the tabs. | ||
children | ChildrenOrFunction | Default: — |
| The children of the component. A function may be provided to alter the children based on component state. | ||
selectedKey | Key | null | Default: — |
| The currently selected key in the collection (controlled). | ||
defaultSelectedKey | Key | Default: — |
| The initial selected key in the collection (uncontrolled). | ||
onSelectionChange | | Default: — |
| Handler that is called when the selection changes. | ||
disabledKeys | Iterable | Default: — |
| The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | ||
Default className: react-aria-Tabs
| Render Prop | CSS Selector |
|---|---|
orientation | CSS 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>.
| Name | Type | |
|---|---|---|
children | ReactNode | | |
| The contents of the collection. | ||
items | Iterable | |
| Item objects in the collection. | ||
dependencies | ReadonlyArray | |
| Values that should invalidate the item cache when using dynamic collections. | ||
Default className: react-aria-TabList
| Render Prop | CSS Selector |
|---|---|
orientation | CSS Selector: [data-orientation="horizontal | vertical"]
|
| The orientation of the tab list. | |
state | CSS Selector: — |
| State of the tab list. | |
Tab
A Tab provides a title for an individual item within a TabList.
| Name | Type | |
|---|---|---|
id | Key | |
| The unique id of the tab. | ||
isDisabled | boolean | |
| Whether the tab is disabled. | ||
children | ChildrenOrFunction | |
| The children of the component. A function may be provided to alter the children based on component state. | ||
Default className: react-aria-Tab
| Render Prop | CSS Selector |
|---|---|
isHovered | CSS Selector: [data-hovered]
|
| Whether the tab is currently hovered with a mouse. | |
isPressed | CSS Selector: [data-pressed]
|
| Whether the tab is currently in a pressed state. | |
isSelected | CSS Selector: [data-selected]
|
| Whether the tab is currently selected. | |
isFocused | CSS Selector: [data-focused]
|
| Whether the tab is currently focused. | |
isFocusVisible | CSS Selector: [data-focus-visible]
|
| Whether the tab is currently keyboard focused. | |
isDisabled | CSS Selector: [data-disabled]
|
| Whether the tab is disabled. | |
TabPanels
Groups multiple <TabPanel> elements, and provides CSS variables for animated transitions.
| Name | Type | |
|---|---|---|
children | ReactNode | | |
| The contents of the collection. | ||
items | Iterable | |
| Item objects in the collection. | ||
dependencies | ReadonlyArray | |
| Values that should invalidate the item cache when using dynamic collections. | ||
disabledKeys | Iterable | |
| 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.
| Name | Type | Default |
|---|---|---|
shouldForceMount | boolean | Default: 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. | ||
id | Key | Default: — |
| The unique id of the tab. | ||
children | ChildrenOrFunction | 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 Prop | CSS Selector |
|---|---|
isFocused | CSS Selector: [data-focused]
|
| Whether the tab panel is currently focused. | |
isFocusVisible | CSS Selector: [data-focus-visible]
|
| Whether the tab panel is currently keyboard focused. | |
isInert | CSS 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. | |
isEntering | CSS Selector: [data-entering]
|
| Whether the tab panel is currently entering. Use this to apply animations. | |
isExiting | CSS Selector: [data-exiting]
|
| Whether the tab panel is currently exiting. Use this to apply animations. | |
state | CSS Selector: — |
| State of the tab list. | |