XDSTabList@xds/core · TabList
Preview coming soon

Usage

TabList provides tab-style navigation for organizing content into categorized sections. Use it to let users switch between related views without leaving the page, with overflow items handled by a built-in "more" menu.

Best practices

GuidancePractices
DoKeep tab labels short and descriptive so users can quickly scan available sections.
DoUse XDSTabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen.
DoWhen using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don’t overpower the tab row.
Don'tUse tabs for sequential steps or workflows — use a stepper or wizard pattern instead.
Don'tPlace more than 6–8 visible tabs before the overflow menu — prioritize the most important categories.
Don'tConfuse TabList with XDSSegmentedControl or XDSToggleButton. TabList is for navigation between views. SegmentedControl and ToggleButton are input controls — SegmentedControl always has exactly one selected option, while ToggleButton can be toggled on or off.

Anatomy

ElementDescription
Left ContentMost important area; hugs content width.
Center-Fill ContentStretches to fill available space.
Right ContentHugs content width.

Import

ts
import {XDSTabList} from '@xds/core/TabList'

Props

PropTypeDescription
valuerequired
stringThe currently selected tab value.
onChangerequired
(value: string) => voidCallback fired when a tab is selected.
childrenrequired
ReactNodeXDSTab and XDSTabMenu items to render inside the nav.
size
'sm' | 'md' | 'lg' (default: 'md')Size variant applied to all child tabs.
hasDivider
boolean (default: false)Whether to show a bottom border divider under the tab list.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}.

Sub-components

TabList is a compound component with 3 sub-components.

XDSTab

Individual tab item that renders as a button or an anchor link, with selected-state styling and optional icons.
PropTypeDescription
valuerequired
stringUnique value for this tab, matched against XDSTabListContext.value.
labelrequired
stringVisible label text for this tab.
href
stringURL to navigate to; when provided, the tab renders as an anchor element.
as
XDSLinkComponentTypeCustom component to render instead of <a> for link tabs. Overrides the XDSLinkProvider default. Only applies when href is provided.
icon
ReactNodeIcon element shown when the tab is not selected.
selectedIcon
ReactNodeIcon element shown when the tab is selected; falls back to icon if not provided.
endContent
ReactNodeContent rendered after the label, such as a badge count or status dot.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}.

XDSTabList

Nav wrapper that provides XDSTabListContext (value, onChange, size) to XDSTab and XDSTabMenu children.
PropTypeDescription
valuerequired
stringThe currently selected tab value.
onChangerequired
(value: string) => voidCallback fired when a tab is selected.
childrenrequired
ReactNodeXDSTab and XDSTabMenu items to render inside the nav.
size
'sm' | 'md' | 'lg' (default: 'md')Size variant applied to all child tabs.
hasDivider
boolean (default: false)Whether to show a bottom border divider under the tab list.
xstyle
StyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}.

XDSTabMenu

Overflow menu trigger that opens a dropdown of additional tab options, showing the selected option's label as the trigger text.
PropTypeDescription
labelrequired
stringLabel for the trigger button (shown when no option is selected) and the dropdown heading divider.
optionsrequired
XDSTabMenuOption[]Array of menu options rendered in the dropdown.

Examples

Common configurations, variations, and states.
TabList — Fill LayoutTabs that stretch to fill the available width with a bottom divider.
tsx
'use client';
import {useState} from 'react';
import {XDSTabList, XDSTab} from '@xds/core/TabList';
export default function TabListTabsFillLayout() {
const [value, setValue] = useState('home');
return (
<div style={{width: 500}}>
<XDSTabList value={value} onChange={setValue} layout="fill" hasDivider>
<XDSTab value="home" label="Home" />
<XDSTab value="projects" label="Projects" />
<XDSTab value="settings" label="Settings" />
</XDSTabList>
</div>
);
}
TabList — With ActionsPage header pattern with tabs on the left and action buttons pushed to the right. When hasDivider is true, pair with a smaller button size (sm) so actions don\u2019t overpower the tab row.
tsx
'use client';
import {useState} from 'react';
import {XDSTabList, XDSTab} from '@xds/core/TabList';
import {XDSButton} from '@xds/core/Button';
const FilterIcon = (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
width="100%"
height="100%">
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3Z"
/>
</svg>
);
const PlusIcon = (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
width="100%"
height="100%">
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 4.5v15m7.5-7.5h-15"
/>
</svg>
);
export default function TabListTabsWithActions() {
const [value, setValue] = useState('all');
return (
<XDSTabList value={value} onChange={setValue} size="lg" hasDivider>
<XDSTab value="all" label="All items" />
<XDSTab value="active" label="Active" />
<XDSTab value="archived" label="Archived" />
<div
style={{
marginInlineStart: 'auto',
display: 'flex',
alignItems: 'center',
gap: 4,
}}>
<XDSButton
label="Filter"
variant="ghost"
size="sm"
icon={FilterIcon}
isIconOnly
/>
<XDSButton
label="New item"
variant="primary"
size="sm"
icon={PlusIcon}
/>
</div>
</XDSTabList>
);
}
TabList — With IconsTabs with leading icons alongside text labels.
tsx
'use client';
import {useState} from 'react';
import {XDSTabList, XDSTab} from '@xds/core/TabList';
const HomeIcon = (
<svg viewBox="0 0 16 16" fill="currentColor" width="100%" height="100%">
<path d="M8.543 2.232a.75.75 0 0 0-1.085 0l-5.25 5.5A.75.75 0 0 0 2.75 9H4v4a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-2h1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V9h1.25a.75.75 0 0 0 .543-1.268l-5.25-5.5Z" />
</svg>
);
const CogIcon = (
<svg viewBox="0 0 16 16" fill="currentColor" width="100%" height="100%">
<path
fillRule="evenodd"
d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-.816a.5.5 0 0 1 .67.087l.774.774a.5.5 0 0 1 .087.67l-.816 1.321c.25.416.442.872.562 1.356l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l.816 1.321a.5.5 0 0 1-.087.67l-.774.774a.5.5 0 0 1-.67.087l-1.321-.816c-.416.25-.872.442-1.356.562l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562l-1.321.816a.5.5 0 0 1-.67-.087l-.774-.774a.5.5 0 0 1-.087-.67l.816-1.321a4.972 4.972 0 0 1-.562-1.356l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356l-.816-1.321a.5.5 0 0 1 .087-.67l.774-.774a.5.5 0 0 1 .67-.087l1.321.816c.416-.25.872-.442 1.356-.562l.17-1.699ZM8 10.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z"
clipRule="evenodd"
/>
</svg>
);
export default function TabListTabsWithIcons() {
const [value, setValue] = useState('home');
return (
<XDSTabList value={value} onChange={setValue}>
<XDSTab value="home" label="Home" icon={HomeIcon} />
<XDSTab value="settings" label="Settings" icon={CogIcon} />
</XDSTabList>
);
}
TabList — With Overflow MenuTab list with a dropdown menu for additional items that do not fit inline.
tsx
'use client';
import {useState} from 'react';
import {XDSTabList, XDSTab, XDSTabMenu} from '@xds/core/TabList';
export default function TabListTabsWithMenu() {
const [value, setValue] = useState('home');
return (
<XDSTabList value={value} onChange={setValue}>
<XDSTab value="home" label="Home" />
<XDSTab value="projects" label="Projects" />
<XDSTabMenu
label="More"
options={[
{value: 'analytics', label: 'Analytics'},
{value: 'reports', label: 'Reports'},
{value: 'billing', label: 'Billing'},
]}
/>
</XDSTabList>
);
}
TabList \u2014 With BadgeTabs with notification badge counts rendered via endContent. Uses error variant for urgent counts and neutral for informational ones.
tsx
'use client';
import {useState} from 'react';
import {XDSTabList, XDSTab} from '@xds/core/TabList';
import {XDSBadge} from '@xds/core/Badge';
export default function TabListTabsWithBadge() {
const [value, setValue] = useState('inbox');
return (
<XDSTabList value={value} onChange={setValue}>
<XDSTab
value="inbox"
label="Inbox"
endContent={<XDSBadge variant="error" label="5" />}
/>
<XDSTab value="sent" label="Sent" />
<XDSTab
value="drafts"
label="Drafts"
endContent={<XDSBadge variant="neutral" label="2" />}
/>
</XDSTabList>
);
}
TabList \u2014 With Status DotTabs with status dot indicators rendered via endContent to show live environment health at a glance.
tsx
'use client';
import {useState} from 'react';
import {XDSTabList, XDSTab} from '@xds/core/TabList';
import {XDSStatusDot} from '@xds/core/StatusDot';
export default function TabListTabsWithStatusDot() {
const [value, setValue] = useState('production');
return (
<XDSTabList value={value} onChange={setValue}>
<XDSTab
value="production"
label="Production"
endContent={<XDSStatusDot variant="positive" label="Healthy" />}
/>
<XDSTab
value="staging"
label="Staging"
endContent={<XDSStatusDot variant="warning" label="Degraded" />}
/>
<XDSTab value="development" label="Development" />
</XDSTabList>
);
}

Showcase source

tsx
'use client';
import {XDSTabList, XDSTab} from '@xds/core/TabList';
export default function TabListShowcase() {
return (
<XDSTabList value="home" onChange={() => {}}>
<XDSTab value="home" label="Home" />
<XDSTab value="projects" label="Projects" />
<XDSTab value="settings" label="Settings" />
</XDSTabList>
);
}