Components
Tabs
Switch between related sections of content within the same context
About
Tabs is built on React Aria and styled with Thumbprint v2 semantic tokens, so it automatically supports light and dark mode. It is a compositional component: render a single TabList containing one Tab per section, followed by a TabPanels containing one TabPanel per tab. Each Tab is paired with its TabPanel by a matching id. TabList can alternatively render its tabs from an array via the tabs prop.
Basic tabs
Selecting a tab reveals its associated panel. When selectedKey is omitted, the component is uncontrolled: React Aria manages the selection state internally, selecting the first tab (or defaultSelectedKey) initially and updating the selection as the user interacts — no state management is required.
<Tabs> <TabList accessibilityLabel="Account sections"> <Tab id="overview">Overview</Tab> <Tab id="reviews">Reviews</Tab> <Tab id="photos">Photos</Tab> </TabList> <TabPanels> <TabPanel id="overview">An overview of the pro.</TabPanel> <TabPanel id="reviews">What customers are saying.</TabPanel> <TabPanel id="photos">A gallery of past work.</TabPanel> </TabPanels> </Tabs>
Controlled selection
Provide selectedKey and onSelectionChange to control which tab is selected. The onSelectionChange callback receives the id of the newly selected tab.
function TabsExample() { const [selectedKey, setSelectedKey] = React.useState('overview'); return ( <Tabs selectedKey={selectedKey} onSelectionChange={setSelectedKey}> <TabList accessibilityLabel="Account sections"> <Tab id="overview">Overview</Tab> <Tab id="reviews">Reviews</Tab> <Tab id="photos">Photos</Tab> </TabList> <TabPanels> <TabPanel id="overview">An overview of the pro.</TabPanel> <TabPanel id="reviews">What customers are saying.</TabPanel> <TabPanel id="photos">A gallery of past work.</TabPanel> </TabPanels> </Tabs> ); }
Tabs from an array
As an alternative to composing Tab children, pass TabList an array of tab props via the tabs prop. Provide either children or tabs, not both.
<Tabs> <TabList accessibilityLabel="Account sections" tabs={[ { id: 'overview', children: 'Overview' }, { id: 'reviews', children: 'Reviews', count: 12 }, { id: 'photos', children: 'Photos' }, ]} /> <TabPanels> <TabPanel id="overview">An overview of the pro.</TabPanel> <TabPanel id="reviews">What customers are saying.</TabPanel> <TabPanel id="photos">A gallery of past work.</TabPanel> </TabPanels> </Tabs>
Tabs with icons
Pass an icon to a Tab. By default the icon is stacked above the label (iconPlacement="stacked"); set iconPlacement="inline" to place it beside the label instead.
<Tabs> <TabList accessibilityLabel="Account sections"> <Tab id="overview" icon={<ContentModifierListSmall />}> Overview </Tab> <Tab id="reviews" icon={<InputsStarSmall />}> Reviews </Tab> </TabList> <TabPanels> <TabPanel id="overview">An overview of the pro.</TabPanel> <TabPanel id="reviews">What customers are saying.</TabPanel> </TabPanels> </Tabs>
Count pill
The count prop renders a neutral pill after the label, typically a count of the items in that section.
<Tabs> <TabList accessibilityLabel="Inbox sections"> <Tab id="all" count={128}>All</Tab> <Tab id="unread" count={12}>Unread</Tab> <Tab id="archived" count={0}>Archived</Tab> </TabList> <TabPanels> <TabPanel id="all">All messages.</TabPanel> <TabPanel id="unread">Unread messages.</TabPanel> <TabPanel id="archived">Archived messages.</TabPanel> </TabPanels> </Tabs>
Alert badge
The badge prop renders a small alert badge after the label, drawing attention to a section that needs the user’s attention.
<Tabs> <TabList accessibilityLabel="Inbox sections"> <Tab id="messages" badge={3}>Messages</Tab> <Tab id="alerts" badge={88}>Alerts</Tab> <Tab id="settings">Settings</Tab> </TabList> <TabPanels> <TabPanel id="messages">Your messages.</TabPanel> <TabPanel id="alerts">Your alerts.</TabPanel> <TabPanel id="settings">Your settings.</TabPanel> </TabPanels> </Tabs>
Divider
Set showBorder on the TabList to render a full-width divider beneath the tab strip. The selected tab’s indicator sits on top of the divider.
<Tabs> <TabList accessibilityLabel="Account sections" showBorder> <Tab id="overview">Overview</Tab> <Tab id="reviews">Reviews</Tab> <Tab id="photos">Photos</Tab> </TabList> <TabPanels> <TabPanel id="overview">An overview of the pro.</TabPanel> <TabPanel id="reviews">What customers are saying.</TabPanel> <TabPanel id="photos">A gallery of past work.</TabPanel> </TabPanels> </Tabs>
Overflow
When there are more tabs than fit the container — common on small screens and mobile web — the tab strip scrolls horizontally. The scrollbar is hidden: users scroll by touch or trackpad, selecting a partially visible tab brings it fully into view, and keyboard focus automatically scrolls off-screen tabs into view. No extra props are needed. Resize the browser to watch the strip below overflow and scroll.
<Tabs> <TabList accessibilityLabel="Profile sections" showBorder> <Tab id="overview">Overview</Tab> <Tab id="reviews" count={128}>Reviews</Tab> <Tab id="photos">Photos</Tab> <Tab id="services">Services offered</Tab> <Tab id="credentials">Credentials</Tab> <Tab id="availability">Availability</Tab> <Tab id="pricing">Pricing</Tab> <Tab id="faq">Frequently asked questions</Tab> <Tab id="similar">Similar pros</Tab> <Tab id="service-area">Service area</Tab> <Tab id="promotions" count={2}>Promotions</Tab> <Tab id="contact">Contact</Tab> </TabList> <TabPanels> <TabPanel id="overview">An overview of the pro.</TabPanel> <TabPanel id="reviews">What customers are saying.</TabPanel> <TabPanel id="photos">A gallery of past work.</TabPanel> <TabPanel id="services">Services this pro offers.</TabPanel> <TabPanel id="credentials">Licenses and certifications.</TabPanel> <TabPanel id="availability">When this pro is available.</TabPanel> <TabPanel id="pricing">Typical project pricing.</TabPanel> <TabPanel id="faq">Answers to common questions.</TabPanel> <TabPanel id="similar">Other pros like this one.</TabPanel> <TabPanel id="service-area">Where this pro works.</TabPanel> <TabPanel id="promotions">Current deals and offers.</TabPanel> <TabPanel id="contact">How to reach this pro.</TabPanel> </TabPanels> </Tabs>
Responsive tabs
To show or hide tabs at certain viewport sizes, conditionally render the Tab and its TabPanel — for example with the useBreakpoint hook from @lib/hooks, as below. Avoid hiding a tab with CSS (display: none): the tab stays in the collection, so arrow-key navigation tries to move focus to the hidden element and breaks. The isSmall === false check matters because isSmall is undefined until hydration completes. Resize the browser below the small breakpoint (480px) to watch the Photos tab drop out of this live example.
function ResponsiveTabs() { const { isSmall } = useBreakpoint(); return ( <Tabs> <TabList accessibilityLabel="Account sections"> <Tab id="overview">Overview</Tab> <Tab id="reviews">Reviews</Tab> {isSmall === false ? <Tab id="photos">Photos</Tab> : null} </TabList> <TabPanels> <TabPanel id="overview">An overview of the pro.</TabPanel> <TabPanel id="reviews">What customers are saying.</TabPanel> {isSmall === false ? ( <TabPanel id="photos">A gallery of past work.</TabPanel> ) : null} </TabPanels> </Tabs> ); }
Props
Tabs
childrenrequiredThe
Tabs andTabPanels to render. Compose a singleTabListfollowed by aTabPanelscontaining oneTabPanelper tab.TypeReact.ReactNodeselectedKeyThe currently selected tab, matching the
idof aTab. Use together withonSelectionChangefor a controlled component. When omitted, the component is uncontrolled: React Aria manages the selection state internally, starting atdefaultSelectedKey(or the first tab) and updating it as the user selects tabs — noonSelectionChangehandler or state management is required. Maps to React Aria'sselectedKey.Typestring | numberdefaultSelectedKeyThe tab selected initially when the component is uncontrolled (no
selectedKey), matching theidof aTab. Has no effect whenselectedKeyis provided. Maps to React Aria'sdefaultSelectedKey.Typestring | numberonSelectionChangeFunction that runs when the selected tab changes. It receives the
idof the newly selected tab. Maps to React Aria'sonSelectionChange.Type(key: string | number) => voidkeyboardActivationWhether tabs are activated automatically on focus (
'automatic', the default) or only when selected by the user ('manual'). Maps to React Aria'skeyboardActivation.Type'automatic' | 'manual'idThe
idis added to the underlying element as an HTML attribute.TypestringdataTestIdA selector hook into the React component for use in automated testing environments.
Typestring
Tab
idrequiredUniquely identifies the tab and pairs it with its
TabPanel. Maps to React Aria'sid.Typestring | numberchildrenText or elements that appear within the tab label.
TypeReact.ReactNodeiconAn optional icon rendered above the label (
iconPlacement="stacked", the default) or beside it (iconPlacement="inline").TypeReact.ReactNodeiconPlacementControls whether the
iconsits above the label ('stacked', the default) or beside it ('inline'). Maps to the[data-icon-placement]attribute.Type'inline' | 'stacked'Default'stacked'countOptional content rendered in a neutral pill after the label, typically a count.
TypeReact.ReactNodebadgeOptional content rendered in an alert badge after the label, typically a notification count.
TypeReact.ReactNodehrefRenders the tab as a link to the given URL. Maps to React Aria's
href.TypestringdataTestIdA selector hook into the React component for use in automated testing environments.
Typestring
TabList
childrenThe
Tabs to render within the list. Provide eitherchildrenortabs, not both.TypeReact.ReactNodetabsRenders the list from an array of
TabPropsas an alternative to composingTabchildren. Provide eitherchildrenortabs, not both.TypeTabProps[]accessibilityLabelAccessible label for the tab list. Required for assistive technologies to describe the set of tabs. Maps to React Aria's
aria-label.TypestringshowBorderRenders a full-width divider beneath the tab strip. Maps to Figma's
showBorderand the[data-show-border]attribute.TypebooleanDefaultfalsedataTestIdA selector hook into the React component for use in automated testing environments.
Typestring
TabPanels
childrenrequiredThe
TabPanels to render, one perTab.TypeReact.ReactNodedataTestIdA selector hook into the React component for use in automated testing environments.
Typestring
TabPanel
idrequiredMatches the
idof theTabthis panel belongs to. Maps to React Aria'sid.Typestring | numberchildrenThe content shown when the associated tab is selected.
TypeReact.ReactNodedataTestIdA selector hook into the React component for use in automated testing environments.
Typestring