XDSTabList@xds/core · TabList
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
| Guidance | Practices |
|---|---|
| Do | Keep tab labels short and descriptive so users can quickly scan available sections. |
| Do | Use XDSTabMenu to group overflow items when horizontal space is limited rather than scrolling tabs off-screen. |
| Do | When using hasDivider with action buttons alongside tabs, use a smaller button size (sm) so the actions don’t overpower the tab row. |
| Don't | Use tabs for sequential steps or workflows — use a stepper or wizard pattern instead. |
| Don't | Place more than 6–8 visible tabs before the overflow menu — prioritize the most important categories. |
| Don't | Confuse 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
| Element | Description | |
|---|---|---|
| Left Content | Most important area; hugs content width. | |
| Center-Fill Content | Stretches to fill available space. | |
| Right Content | Hugs content width. |
Import
tsimport {XDSTabList} from '@xds/core/TabList'
Props
| Prop | Type | Description |
|---|---|---|
valuerequired | string | The currently selected tab value. |
onChangerequired | (value: string) => void | Callback fired when a tab is selected. |
childrenrequired | ReactNode | XDSTab 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 | StyleXStyles | StyleX 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.| Prop | Type | Description |
|---|---|---|
valuerequired | string | Unique value for this tab, matched against XDSTabListContext.value. |
labelrequired | string | Visible label text for this tab. |
href | string | URL to navigate to; when provided, the tab renders as an anchor element. |
as | XDSLinkComponentType | Custom component to render instead of <a> for link tabs. Overrides the XDSLinkProvider default. Only applies when href is provided. |
icon | ReactNode | Icon element shown when the tab is not selected. |
selectedIcon | ReactNode | Icon element shown when the tab is selected; falls back to icon if not provided. |
endContent | ReactNode | Content rendered after the label, such as a badge count or status dot. |
xstyle | StyleXStyles | StyleX 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.| Prop | Type | Description |
|---|---|---|
valuerequired | string | The currently selected tab value. |
onChangerequired | (value: string) => void | Callback fired when a tab is selected. |
childrenrequired | ReactNode | XDSTab 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 | StyleXStyles | StyleX 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.| Prop | Type | Description |
|---|---|---|
labelrequired | string | Label 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 = (<svgviewBox="0 0 24 24"fill="none"stroke="currentColor"strokeWidth={2}width="100%"height="100%"><pathstrokeLinecap="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 = (<svgviewBox="0 0 24 24"fill="none"stroke="currentColor"strokeWidth={2}width="100%"height="100%"><pathstrokeLinecap="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" /><divstyle={{marginInlineStart: 'auto',display: 'flex',alignItems: 'center',gap: 4,}}><XDSButtonlabel="Filter"variant="ghost"size="sm"icon={FilterIcon}isIconOnly/><XDSButtonlabel="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%"><pathfillRule="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" /><XDSTabMenulabel="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}><XDSTabvalue="inbox"label="Inbox"endContent={<XDSBadge variant="error" label="5" />}/><XDSTab value="sent" label="Sent" /><XDSTabvalue="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}><XDSTabvalue="production"label="Production"endContent={<XDSStatusDot variant="positive" label="Healthy" />}/><XDSTabvalue="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>);}