Tabs
Switches between views of the same subject without leaving the page.
<Tabs defaultValue="overview" className="w-full max-w-md">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="evidence">Evidence</TabsTrigger>
<TabsTrigger value="history">History</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="text-text-secondary">
Lookalike domain acme-login.co, first seen 21 October, risk score 87.
</TabsContent>
<TabsContent value="evidence" className="text-text-secondary">
2 screenshots, 1 WHOIS record and the MX records of the domain.
</TabsContent>
<TabsContent value="history" className="text-text-secondary">
Created by domain monitoring. Assigned to the SOC team.
</TabsContent>
</Tabs>Usage
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@zenoxai/ui/components/tabs"
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="evidence">Evidence</TabsTrigger>
</TabsList>
<TabsContent value="overview">...</TabsContent>
<TabsContent value="evidence">...</TabsContent>
</Tabs>
Use tabs for views of equal weight that the user switches between: the sections of an alert,
the status pivots of a list. Each TabsTrigger and its TabsContent share a value. Keep
labels to one or two words and keep the set to what fits on one line.
The default variant looks like a segmented Toggle Group: the same bordered track, and the active tab takes the same selection colors, a primary-soft fill and text in a primary inset ring, while hover stays neutral. Choose between the two by what they switch: tabs swap panels, a toggle group changes what one view shows.
Do not use tabs for steps the user must complete in order, or to navigate between pages: use links. For a short set of options that filter a list in place, use the toggle group.
Examples
Line
variant="line" on TabsList is the tab row under the title of every Zenox module page. The
list spans its container and draws its own bottom rule, and the active tab is marked by primary
text over an underline in the primary color. Put counts in a soft Badge
after the label. When the tabs do not fit, on a phone for example, the row scrolls sideways and
keeps its rule; focusing a tab scrolls it into view.
<Tabs defaultValue="open" className="w-full">
<TabsList variant="line">
<TabsTrigger value="open">
Open
<Badge variant="soft" tone="neutral">
24
</Badge>
</TabsTrigger>
<TabsTrigger value="in-progress">
In progress
<Badge variant="soft" tone="neutral">
6
</Badge>
</TabsTrigger>
<TabsTrigger value="resolved">Resolved</TabsTrigger>
<TabsTrigger value="dismissed">Dismissed</TabsTrigger>
</TabsList>
<TabsContent value="open" className="pt-2 text-text-secondary">
24 alerts are waiting for triage.
</TabsContent>
<TabsContent value="in-progress" className="pt-2 text-text-secondary">
6 alerts are assigned to an analyst.
</TabsContent>
<TabsContent value="resolved" className="pt-2 text-text-secondary">
Alerts closed in the last 90 days.
</TabsContent>
<TabsContent value="dismissed" className="pt-2 text-text-secondary">
Alerts marked as false positives.
</TabsContent>
</Tabs>Vertical
Set orientation="vertical" on Tabs for settings pages with more sections than fit in a
row. The arrow keys follow the orientation: Up and Down move between tabs.
<Tabs
defaultValue="notifications"
orientation="vertical"
className="w-full max-w-md"
>
<TabsList>
<TabsTrigger value="notifications">
<BellIcon />
Notifications
</TabsTrigger>
<TabsTrigger value="members">
<UsersIcon />
Members
</TabsTrigger>
<TabsTrigger value="api-keys">
<KeyRoundIcon />
API keys
</TabsTrigger>
</TabsList>
<TabsContent value="notifications" className="text-text-secondary">
Send an email when a critical alert opens.
</TabsContent>
<TabsContent value="members" className="text-text-secondary">
12 members in the Acme Corp tenant.
</TabsContent>
<TabsContent value="api-keys" className="text-text-secondary">
2 active keys. The oldest was created 140 days ago.
</TabsContent>
</Tabs>Accessibility
- The list has
role="tablist", each triggerrole="tab"witharia-selected, and each panelrole="tabpanel"labelled by its tab. Arrow keys move between tabs, Home and End jump to the first and last. - By default the arrow keys move focus without selecting; Enter or Space selects. Set
activateOnFocusonTabsListto select on focus, only when panels render without delay. - Base UI flips Left and Right in a right-to-left layout. See Direction.
API reference
Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.
Tabs
Tabs switches between views of one subject that share a place on the page, such as the overview and the history of a record. Compose a TabsList with a TabsTrigger per view, and a TabsContent per view matched by `value`. For a control that filters the same list, use ToggleGroup.
| Prop | Type | Default |
|---|---|---|
defaultValue | anyThe default value. Use when the component is not controlled. When the value is `null`, no Tab will be active. | 0 |
onValueChange | ((value: any, eventDetails: TabsRootChangeEventDetails) => void)Callback invoked when new value is being set. | - |
orientation | "horizontal" | "vertical"The component orientation (layout flow direction). | horizontal |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsRootState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
value | anyThe value of the currently active `Tab`. Use when the component is controlled. When the value is `null`, no Tab will be active. | - |
TabsList
TabsList is the row of TabsTrigger buttons.
| Prop | Type | Default |
|---|---|---|
activateOnFocus | booleanWhether to automatically change the active tab on arrow key focus. Otherwise, tabs will be activated using <kbd>Enter</kbd> or <kbd>Space</kbd> key press. | false |
loopFocus | booleanWhether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. | true |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsListState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
variant | "line" | "default" | null`default` is a bordered track whose active tab takes the selection colors, the same control as a segmented ToggleGroup; `line` is a full-width bar on a bottom rule, with the active tab underlined, for page headers, sheets and status pivots. | default |
TabsTrigger
| Prop | Type | Default |
|---|---|---|
disabled | booleanWhether the Tab is disabled. | - |
nativeButton | booleanWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | true |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsTabState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
value* | anyThe value of the Tab. | - |
TabsContent
| Prop | Type | Default |
|---|---|---|
keepMounted | booleanWhether to keep the HTML element in the DOM while the panel is hidden. | false |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsPanelState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
value* | anyThe value of the TabPanel. It will be shown when the Tab with the corresponding value is active. | - |
TabsIndicator
TabsIndicator is a bar that slides to the active tab. Place it inside a TabsList with `variant="line"` for an animated underline; Base UI sets the `--active-tab-{width,height,left,top}` variables it moves with.
| Prop | Type | Default |
|---|---|---|
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsIndicatorState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
renderBeforeHydration | booleanWhether to render itself before React hydrates. This minimizes the time that the indicator isn't visible after server-side rendering. | false |