Skip to content

Tabs

Controlled tab wrapper. Uses `tablist` / `tabpanel` semantics, so reach for it when selecting an option shows or hides associated content panels. To pick a value without swapping panels — a styled radio group for filters, settings, or view modes — use SegmentedControl instead.

Overview content
function TabsStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState(args.value);
return (
<>
<Tabs {...args} onChange={setValue} value={value}>
<Tab
controls="overview-panel"
id="overview-tab"
label="Overview"
value="overview"
/>
<Tab
controls="activity-panel"
endContent={<Badge color="info" label="3" />}
id="activity-tab"
label="Activity"
value="activity"
/>
<Tab
controls="settings-panel"
icon={Settings}
id="settings-tab"
label="Settings"
value="settings"
/>
<Tab isDisabled label="Disabled" value="disabled" />
<TabMenu
id="more-tabs-tab"
label="More"
options={[
{
icon: BarChart3,
label: 'Analytics',
value: 'analytics',
},
{label: 'Reports', value: 'reports'},
]}
/>
</Tabs>
<div
aria-labelledby="overview-tab"
hidden={value !== 'overview'}
id="overview-panel"
role="tabpanel">
Overview content
</div>
<div
aria-labelledby="activity-tab"
hidden={value !== 'activity'}
id="activity-panel"
role="tabpanel">
Activity content
</div>
<div
aria-labelledby="settings-tab"
hidden={value !== 'settings'}
id="settings-panel"
role="tabpanel">
Settings content
</div>
<div
aria-labelledby="more-tabs-tab"
hidden={value !== 'analytics'}
id="analytics-panel"
role="tabpanel">
Analytics content
</div>
<div
aria-labelledby="more-tabs-tab"
hidden={value !== 'reports'}
id="reports-panel"
role="tabpanel">
Reports content
</div>
</>
);
}
<TabsStory {...args} />
function PackageManagersStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState('yarn');
return (
<Tabs {...args} hasDivider onChange={setValue} value={value}>
<Tab label="npm" value="npm" />
<Tab label="pnpm" value="pnpm" />
<Tab label="yarn" value="yarn" />
</Tabs>
);
}
<PackageManagersStory {...args} />
Overview content
function TabsStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState(args.value);
return (
<>
<Tabs {...args} onChange={setValue} value={value}>
<Tab
controls="overview-panel"
id="overview-tab"
label="Overview"
value="overview"
/>
<Tab
controls="activity-panel"
endContent={<Badge color="info" label="3" />}
id="activity-tab"
label="Activity"
value="activity"
/>
<Tab
controls="settings-panel"
icon={Settings}
id="settings-tab"
label="Settings"
value="settings"
/>
<Tab isDisabled label="Disabled" value="disabled" />
<TabMenu
id="more-tabs-tab"
label="More"
options={[
{
icon: BarChart3,
label: 'Analytics',
value: 'analytics',
},
{label: 'Reports', value: 'reports'},
]}
/>
</Tabs>
<div
aria-labelledby="overview-tab"
hidden={value !== 'overview'}
id="overview-panel"
role="tabpanel">
Overview content
</div>
<div
aria-labelledby="activity-tab"
hidden={value !== 'activity'}
id="activity-panel"
role="tabpanel">
Activity content
</div>
<div
aria-labelledby="settings-tab"
hidden={value !== 'settings'}
id="settings-panel"
role="tabpanel">
Settings content
</div>
<div
aria-labelledby="more-tabs-tab"
hidden={value !== 'analytics'}
id="analytics-panel"
role="tabpanel">
Analytics content
</div>
<div
aria-labelledby="more-tabs-tab"
hidden={value !== 'reports'}
id="reports-panel"
role="tabpanel">
Reports content
</div>
</>
);
}
<TabsStory {...args} />
<div style={{display: 'grid', gap: 16}}>
<Tabs {...args} onChange={() => {}} size="sm" value="overview">
<Tab label="Overview" value="overview" />
<Tab label="Activity" value="activity" />
</Tabs>
<Tabs {...args} onChange={() => {}} size="lg" value="overview">
<Tab label="Overview" value="overview" />
<Tab label="Activity" value="activity" />
</Tabs>
</div>
<div style={{display: 'grid', gap: 16}}>
<Tabs {...args} hasDivider={false} onChange={() => {}} value="overview">
<Tab label="Overview" value="overview" />
<Tab label="Activity" value="activity" />
</Tabs>
<Tabs {...args} hasDivider onChange={() => {}} value="overview">
<Tab label="Overview" value="overview" />
<Tab label="Activity" value="activity" />
</Tabs>
</div>
<Tabs {...args} onChange={() => {}} value="overview">
<Tab href="/overview" label="Overview" value="overview" />
<Tab href="/activity" label="Activity" value="activity" />
<Tab href="/settings" label="Settings" value="settings" />
</Tabs>
<Tabs {...args} onChange={() => {}} value="overview">
<Tab as={RouterLink} href="/overview" label="Overview" value="overview" />
<Tab as={RouterLink} href="/activity" label="Activity" value="activity" />
<Tab as={RouterLink} href="/settings" label="Settings" value="settings" />
</Tabs>
function SelectedIconStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState('notifications');
return (
<Tabs {...args} onChange={setValue} value={value}>
<Tab icon={Home} label="Home" value="home" />
<Tab
icon={Bell}
label="Notifications"
selectedIcon={BellRing}
value="notifications"
/>
<Tab icon={Settings} label="Settings" value="settings" />
</Tabs>
);
}
<SelectedIconStory {...args} />
<Tabs {...args} onChange={() => {}} value="overview">
<Tab label="Overview" value="overview" />
<Tab isDisabled label="Activity" value="activity" />
<TabMenu
isDisabled
label="More"
options={[{label: 'Reports', value: 'reports'}]}
/>
</Tabs>
function TabMenuOnlyStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState('analytics');
return (
<Tabs {...args} onChange={setValue} value={value}>
<TabMenu
label="Views"
options={[
{icon: BarChart3, label: 'Analytics', value: 'analytics'},
{icon: FileText, label: 'Reports', value: 'reports'},
{icon: Users, label: 'Audience', value: 'audience'},
]}
/>
</Tabs>
);
}
<TabMenuOnlyStory {...args} />
function ManyTabsStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState('overview');
return (
<Tabs {...args} onChange={setValue} value={value}>
<Tab label="Overview" value="overview" />
<Tab label="Activity" value="activity" />
<Tab label="Members" value="members" />
<Tab label="Files" value="files" />
<Tab label="Billing" value="billing" />
<Tab label="Security" value="security" />
<TabMenu
label="More"
options={[
{label: 'Analytics', value: 'analytics'},
{label: 'Reports', value: 'reports'},
{label: 'Integrations', value: 'integrations'},
]}
/>
</Tabs>
);
}
<ManyTabsStory {...args} />
function ControlledStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState(args.value);
return (
<Tabs {...args} onChange={setValue} value={value}>
<Tab label="Overview" value="overview" />
<Tab label="Activity" value="activity" />
<Tab label="Settings" value="settings" />
</Tabs>
);
}
<ControlledStory {...args} />
Overview content
function WithPanelsStory(args: React.ComponentProps<typeof Tabs>) {
const [value, setValue] = useState('overview');
return (
<>
<Tabs {...args} onChange={setValue} value={value}>
<Tab
controls="manual-overview-panel"
id="manual-overview-tab"
label="Overview"
value="overview"
/>
<Tab
controls="manual-activity-panel"
id="manual-activity-tab"
label="Activity"
value="activity"
/>
</Tabs>
<div
aria-labelledby="manual-overview-tab"
hidden={value !== 'overview'}
id="manual-overview-panel"
role="tabpanel">
Overview content
</div>
<div
aria-labelledby="manual-activity-tab"
hidden={value !== 'activity'}
id="manual-activity-panel"
role="tabpanel">
Activity content
</div>
</>
);
}
<WithPanelsStory {...args} />
<div dir="rtl">
<Tabs {...args} onChange={() => {}} value="overview">
<Tab label="Overview" value="overview" />
<Tab label="Activity" value="activity" />
<Tab label="Settings" value="settings" />
</Tabs>
</div>

Controlled tab wrapper. Uses `tablist` / `tabpanel` semantics, so reach for it when selecting an option shows or hides associated content panels. To pick a value without swapping panels — a styled radio group for filters, settings, or view modes — use SegmentedControl instead.

PropTypeDefaultDescription
children*ReactNodeTab and TabMenu children.
classNamestringAdditional CSS class names applied to the tablist element.
data-testidstringTest ID applied to the tablist element.
hasDividerbooleanfalseWhether to show a bottom divider.
labelstring'Tabs'Accessible label for the tabs.
layout"fill" | "hug"'hug'Tab layout mode.
onChange*(value: string) => voidCalled when a tab is selected.
refRef<HTMLElement>Ref forwarded to the tablist element.
size"sm" | "md" | "lg"'md'Tab size.
styleCSSPropertiesInline styles applied to the tablist element.
value*stringCurrent selected tab value.

A single tab inside `Tabs`.

PropTypeDefaultDescription
asLinkComponentCustom link component used when href is set.
classNamestringAdditional CSS class names applied to the tab.
controlsstringID of the tabpanel controlled by this tab. Use this for in-page tabs, and render the corresponding panel with id={controls}, role="tabpanel", and aria-labelledby pointing to this tab's id.
data-testidstringTest ID applied to the tab.
endContentReactNodeContent rendered after the label.
hrefstringOptional link URL. When set, the tab renders as a link.
iconIconComponentIcon shown before the label.
idstringID applied to the tab element. Provide this with controls when rendering your own tabpanel so the panel can reference the tab with aria-labelledby.
isDisabledbooleanfalseWhether the tab is disabled.
label*stringVisible tab label.
refRef<HTMLElement>Ref forwarded to the tab root.
selectedIconIconComponentIcon shown when selected. Falls back to icon.
styleCSSPropertiesInline styles applied to the tab.
value*stringUnique tab value.

Overflow menu for additional tabs.

PropTypeDefaultDescription
classNamestringAdditional CSS class names applied to the trigger.
data-testidstringTest ID applied to the trigger.
idstringID applied to the menu trigger tab.
isDisabledbooleanfalseWhether the menu trigger is disabled.
label*stringTrigger and menu heading label.
options*ReadonlyArray<TabMenuOption>Menu options.
refRef<HTMLButtonElement>Ref forwarded to the trigger button.
styleCSSPropertiesInline styles applied to the trigger.