Provides the behavior and accessibility implementation for a tab list.
Tabs organize content into multiple sections and allow users to navigate between them.
Tabs provide a list of tabs that a user can select from to switch between multiple tab panels. useTabList, useTab, and useTabPanel can be used to implement these in an accessible way.
Support for mouse, touch, and keyboard interactions on tabs
Support for LTR and RTL keyboard navigation
Support for disabled tabs
Follows the tabs ARIA pattern, semantically linking tabs and their associated tab panels
Focus management for tab panels without any focusable children
Tabs consist of a tab list with one or more visually separated tabs. Each tab has associated content, and only the selected tab's content is shown.
Each tab can be clicked, tapped, or navigated to via arrow keys. Depending on the keyboardActivation prop, the tab can be selected by receiving keyboard focus, or it can be selected with the Enter key.
useTabList returns props to spread onto the tab list container:
State is managed by the useTabListState
hook in @react-stately/tabs. The state object should be passed as an option to useTabList, useTab,
and useTabPanel.
This example displays a basic list of tabs. The currently selected tab receives a tabIndex of 0 while the rest are set to -1 ensuring that the whole tablist is a single tab stop. The selected tab has a different style so it's obvious which one is currently selected. useTab and useTabPanel handle associating the tabs and tab panels for assistive technology. The currently selected tab panel is rendered below the list of tabs. The key prop on the TabPanel element is important to ensure that DOM state (e.g. text field contents) is not shared between unrelated tabs.
import{Item}from'@react-stately/collections';functionTabs(props){let state =useTabListState(props);let ref =React.useRef();let{tabListProps}=useTabList(props, state, ref);return(<divstyle={{height:'150px'}}><div{...tabListProps}ref={ref}style={{display:'flex',borderBottom:'1px solid grey'}}>{[...state.collection].map((item)=>(<Tabkey={item.key}item={item}state={state}/>))}</div><TabPanelkey={state.selectedItem?.key}state={state}/></div>);}functionTab({item, state}){let{key, rendered}= item;let ref =React.useRef();let{tabProps}=useTab({key}, state, ref);let isSelected = state.selectedKey=== key;let isDisabled = state.disabledKeys.has(key);return(<div{...tabProps}ref={ref}style={{padding:'10px',borderBottom: isSelected ?'3px solid var(--blue)':undefined,opacity: isDisabled ?'0.5':undefined}}>{rendered}</div>);}functionTabPanel({state,...props}){let ref =React.useRef();let{tabPanelProps}=useTabPanel(props, state, ref);return(<div{...tabPanelProps}ref={ref}style={{padding:'10px'}}>{state.selectedItem?.props.children}</div>);}<Tabsaria-label="History of Ancient Rome"disabledKeys={['Emp']}><Itemkey="FoR"title="Founding of Rome">
Arma virumque cano, Troiae qui primus ab oris.
</Item><Itemkey="MaR"title="Monarchy and Republic">
Senatus Populusque Romanus.
</Item><Itemkey="Emp"title="Empire">
Alea jacta est.
</Item></Tabs>
import{Item}from'@react-stately/collections';functionTabs(props){let state =useTabListState(props);let ref =React.useRef();let{tabListProps}=useTabList(props, state, ref);return(<divstyle={{height:'150px'}}><div{...tabListProps}ref={ref}style={{display:'flex',borderBottom:'1px solid grey'}}>{[...state.collection].map((item)=>(<Tabkey={item.key}item={item}state={state}/>))}</div><TabPanelkey={state.selectedItem?.key}state={state}/></div>);}functionTab({item, state}){let{key, rendered}= item;let ref =React.useRef();let{tabProps}=useTab({key}, state, ref);let isSelected = state.selectedKey=== key;let isDisabled = state.disabledKeys.has(key);return(<div{...tabProps}ref={ref}style={{padding:'10px',borderBottom: isSelected
?'3px solid var(--blue)':undefined,opacity: isDisabled ?'0.5':undefined}}>{rendered}</div>);}functionTabPanel({state,...props}){let ref =React.useRef();let{tabPanelProps}=useTabPanel(props, state, ref);return(<div{...tabPanelProps}ref={ref}style={{padding:'10px'}}>{state.selectedItem?.props.children}</div>);}<Tabsaria-label="History of Ancient Rome"disabledKeys={['Emp']}><Itemkey="FoR"title="Founding of Rome">
Arma virumque cano, Troiae qui primus ab oris.
</Item><Itemkey="MaR"title="Monarchy and Republic">
Senatus Populusque Romanus.
</Item><Itemkey="Emp"title="Empire">
Alea jacta est.
</Item></Tabs>
import{Item}from'@react-stately/collections';functionTabs(props){let state =useTabListState(
props
);let ref =React.useRef();let{
tabListProps
}=useTabList(
props,
state,
ref
);return(<divstyle={{height:'150px'}}><div{...tabListProps}ref={ref}style={{display:'flex',borderBottom:'1px solid grey'}}>{[...state.collection].map((item)=>(<Tabkey={
item.key}item={item}state={state}/>))}</div><TabPanelkey={
state
.selectedItem?.key}state={state}/></div>);}functionTab({
item,
state
}){let{
key,
rendered
}= item;let ref =React.useRef();let{
tabProps
}=useTab({key},
state,
ref
);let isSelected =
state.selectedKey===
key;let isDisabled = state.disabledKeys.has(
key
);return(<div{...tabProps}ref={ref}style={{padding:'10px',borderBottom: isSelected
?'3px solid var(--blue)':undefined,opacity: isDisabled
?'0.5':undefined}}>{rendered}</div>);}functionTabPanel({
state,...props
}){let ref =React.useRef();let{
tabPanelProps
}=useTabPanel(
props,
state,
ref
);return(<div{...tabPanelProps}ref={ref}style={{padding:'10px'}}>{
state
.selectedItem?.props.children}</div>);}<Tabsaria-label="History of Ancient Rome"disabledKeys={['Emp']}><Itemkey="FoR"title="Founding of Rome">
Arma virumque cano,
Troiae qui primus
ab oris.
</Item><Itemkey="MaR"title="Monarchy and Republic">
Senatus Populusque
Romanus.
</Item><Itemkey="Emp"title="Empire">
Alea jacta est.
</Item></Tabs>
When the tab panel doesn't contain any focusable content, the entire panel is given a tabIndex=0 so that the content can be navigated to with the keyboard. When the tab panel contains focusable content, such as a textfield, then the tabIndex is omitted because the content itself can receive focus.
This example uses the same Tabs component from above. Try navigating from the tabs to the content for each panel using the keyboard.
<Tabsaria-label="Notes app"><Itemkey="item1"title="Jane Doe"><label>
Leave a note for Jane: <inputtype="text"/></label></Item><Itemkey="item2"title="John Doe">
Senatus Populusque Romanus.
</Item><Itemkey="item3"title="Joe Bloggs">
Alea jacta est.
</Item></Tabs>
<Tabsaria-label="Notes app"><Itemkey="item1"title="Jane Doe"><label>
Leave a note for Jane: <inputtype="text"/></label></Item><Itemkey="item2"title="John Doe">
Senatus Populusque Romanus.
</Item><Itemkey="item3"title="Joe Bloggs">
Alea jacta est.
</Item></Tabs>
<Tabsaria-label="Notes app"><Itemkey="item1"title="Jane Doe"><label>
Leave a note for
Jane:{' '}<inputtype="text"/></label></Item><Itemkey="item2"title="John Doe">
Senatus Populusque
Romanus.
</Item><Itemkey="item3"title="Joe Bloggs">
Alea jacta est.
</Item></Tabs>
useTabList handles some aspects of internationalization automatically. For example, keyboard navigation is automatically mirrored for right-to-left languages. You are responsible for localizing all tab labels and content.
In right-to-left languages, the tablist should be mirrored. The first tab is furthest right and the last tab is furthest left. Ensure that your CSS accounts for this.
Provides state management for a Tabs component. Tabs include a TabList which tracks
which tab is currently selected and displays the content associated with that Tab in a TabPanel.