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.
Examples
Section titled “Examples”Default
Section titled “Default”Overview content
Activity content
Settings content
Analytics content
Reports content
<TabsStory {...args} />Overview content
Activity content
Settings content
Analytics content
Reports content
<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>Divider
Section titled “Divider”<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>Link Tabs
Section titled “Link Tabs”<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>Custom Link
Section titled “Custom Link”<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>Selected Icon
Section titled “Selected Icon”<SelectedIconStory {...args} />Disabled
Section titled “Disabled”<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>Tab Menu Only
Section titled “Tab Menu Only”<TabMenuOnlyStory {...args} />Many Tabs
Section titled “Many Tabs”<ManyTabsStory {...args} />Controlled
Section titled “Controlled”<ControlledStory {...args} />With Panels
Section titled “With Panels”Overview content
Activity content
<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.
| Prop | Type | Default | Description |
|---|---|---|---|
children* | ReactNode | — | Tab and TabMenu children. |
className | string | — | Additional CSS class names applied to the tablist element. |
data-testid | string | — | Test ID applied to the tablist element. |
hasDivider | boolean | false | Whether to show a bottom divider. |
label | string | 'Tabs' | Accessible label for the tabs. |
layout | "fill" | "hug" | 'hug' | Tab layout mode. |
onChange* | (value: string) => void | — | Called when a tab is selected. |
ref | Ref<HTMLElement> | — | Ref forwarded to the tablist element. |
size | "sm" | "md" | "lg" | 'md' | Tab size. |
style | CSSProperties | — | Inline styles applied to the tablist element. |
value* | string | — | Current selected tab value. |
A single tab inside `Tabs`.
| Prop | Type | Default | Description |
|---|---|---|---|
as | LinkComponent | — | Custom link component used when href is set. |
className | string | — | Additional CSS class names applied to the tab. |
controls | string | — | ID 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-testid | string | — | Test ID applied to the tab. |
endContent | ReactNode | — | Content rendered after the label. |
href | string | — | Optional link URL. When set, the tab renders as a link. |
icon | IconComponent | — | Icon shown before the label. |
id | string | — | ID applied to the tab element. Provide this with controls when rendering your own tabpanel so the panel can reference the tab with aria-labelledby. |
isDisabled | boolean | false | Whether the tab is disabled. |
label* | string | — | Visible tab label. |
ref | Ref<HTMLElement> | — | Ref forwarded to the tab root. |
selectedIcon | IconComponent | — | Icon shown when selected. Falls back to icon. |
style | CSSProperties | — | Inline styles applied to the tab. |
value* | string | — | Unique tab value. |
TabMenu
Section titled “TabMenu”Overflow menu for additional tabs.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Additional CSS class names applied to the trigger. |
data-testid | string | — | Test ID applied to the trigger. |
id | string | — | ID applied to the menu trigger tab. |
isDisabled | boolean | false | Whether the menu trigger is disabled. |
label* | string | — | Trigger and menu heading label. |
options* | ReadonlyArray<TabMenuOption> | — | Menu options. |
ref | Ref<HTMLButtonElement> | — | Ref forwarded to the trigger button. |
style | CSSProperties | — | Inline styles applied to the trigger. |