Skip to main content
Tabs connects each tab to a related content panel using a shared value. Keep values unique within a root. Use TabButton for route links and adjacent actions.

Usage

Use defaultValue to set the initial selection. The root owns subsequent changes.
Tabs.Tab accepts startIcon, endIcon, badge, and a size of sm or md (default sm). The remaining props retain the installed Base UI contract. Values are not restricted to strings or numbers. Reference values must use the same identity on their tab, panel, and root. A root value of null selects no panel.

Anatomy

Place tabs and an optional indicator inside Tabs.List. Place their panels alongside the list, all within the same Tabs.Root.
Give the list an accessible name and match each tab’s value to its panel’s value. Base UI supplies the tab and panel IDs, aria-controls, and aria-labelledby relationships. When you provide a custom id on Tabs.Panel, set aria-controls on the matching Tabs.Tab to that same ID. The installed Base UI 1.8.0 implementation requires this explicit relationship. Custom tab IDs are registered correctly for the panel’s aria-labelledby. Add one Tabs.Indicator when you want the active underline. List does not insert an indicator.

Controlled state and event details

Pass value and update it with onValueChange to own selection. The callback receives the next value and Base UI event details, including the originating event, reason, activationDirection, and cancel().
User activation has reason none. In uncontrolled roots, automatic fallback can have reason initial, disabled, or missing. Automatic fallback may select null when no enabled tab remains. These automatic changes cannot be canceled. Controlled roots keep selection under the application’s ownership. Cancel a user change before updating controlled state, or use the same callback with an uncontrolled root:

Keyboard activation, orientation, and direction

Tabs.List uses manual activation by default (activateOnFocus={false}). Arrow keys move focus; Enter or Space activates the focused tab. Set activateOnFocus to select a tab when keyboard focus reaches it. Both policies work with controlled and uncontrolled state.
Horizontal lists use Left and Right; vertical lists use Up and Down. Home and End focus the first and last enabled tabs. Disabled tabs are skipped. loopFocus defaults to true; set it to false to stop at either boundary. Use DirectionProvider for RTL keyboard direction and dir="rtl" on the root for layout. Set orientation="vertical" on Root for a vertical list and indicator.

Panel lifetime

Use keepMounted when panel contents must retain their state while inactive. Inactive mounted panels remain hidden and do not occupy layout space.

Panel layout

Panel fills the space left by the list and can shrink below its content on both axes, so it stays within a root of fixed height. To scroll its content, set overflow: auto on the panel, or on a child that fills it, such as a flex: 1 child of a column flex panel.

Native props, render composition, and refs

All five parts preserve native props, event handlers, refs, and Base UI render composition. Their className and style can also be functions of the part’s state. The default elements are a div for Root, List, and Panel, a button for Tab, and a span for Indicator. Refs point to the actual rendered elements.
Keep nativeButton consistent with the element composed for a tab. Use nativeButton={false} for a custom interactive element that is not a native button and forwards the supplied props and ref. A route anchor belongs in TabButton, where the route owns navigation and aria-current.

Visual emphasis

Use highlighted on Tabs.Tab to outline its content independently of selection, such as when its settings are open. Selection remains owned by Tabs.Root.

Props

The generated reference includes the public props for every part. Public part state types and Root change detail/reason types are available from twenty-ui/primitives/navigation.

Tabs.Root

string | ((state: TabsRootState) => string | undefined)
CSS class applied to the element, or a function that returns a class based on the component’s state.
any
default:"0"
The default value. Use when the component is not controlled. When the value is null, no Tab will be active.
((value: any, eventDetails: TabsRootChangeEventDetails) => void)
Callback invoked when new value is being set.The event reason is 'none' for user-initiated changes, such as a click or keyboard navigation; 'initial' for the first automatic selection or fallback in uncontrolled roots when defaultValue is omitted or undefined, including when the implicit initial value is disabled or missing; 'disabled' for automatic fallback when the selected tab becomes disabled in uncontrolled roots; or 'missing' for automatic fallback when the selected tab is removed, or when an explicit defaultValue never matches a mounted tab in uncontrolled roots.For automatic changes, the selected value can be null when no enabled Tab is available as a fallback.Automatic changes cannot be canceled; calling eventDetails.cancel() for 'initial', 'disabled', or 'missing' has no effect.
"horizontal" | "vertical"
default:"horizontal"
The component orientation (layout flow direction).
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.Accepts a ReactElement or a function that returns the element to render.
CSSProperties | ((state: TabsRootState) => CSSProperties | undefined)
Style applied to the element, or a function that returns a style object based on the component’s state.
any
The value of the currently active Tab. Use when the component is controlled. When the value is null, no Tab will be active.

Tabs.List

boolean
default:"false"
Whether 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.
string | ((state: TabsListState) => string | undefined)
CSS class applied to the element, or a function that returns a class based on the component’s state.
boolean
default:"true"
Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys.
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.Accepts a ReactElement or a function that returns the element to render.
CSSProperties | ((state: TabsListState) => CSSProperties | undefined)
Style applied to the element, or a function that returns a style object based on the component’s state.

Tabs.Tab

ReactNode
Content rendered after the label, such as a count.
string | ((state: TabsTabState) => string | undefined)
CSS class applied to the element, or a function that returns a class based on the component’s state.
boolean
Whether the Tab is disabled.If a first Tab on a <Tabs.List> is disabled, it won’t initially be selected. Instead, the next enabled Tab will be selected. However, it does not work like this during server-side rendering, as it is not known during pre-rendering which Tabs are disabled. To work around it, ensure that defaultValue or value on <Tabs.Root> is set to an enabled Tab’s value.
ReactNode
Decorative content after the label and before the badge.
boolean
default:"false"
Emphasizes the tab content without changing selection.
boolean
default:"true"
Whether 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>).
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.Accepts a ReactElement or a function that returns the element to render.
"md" | "sm"
default:"sm"
Visual size of the tab.
ReactNode
Icon rendered before the label.
CSSProperties | ((state: TabsTabState) => CSSProperties | undefined)
Style applied to the element, or a function that returns a style object based on the component’s state.
any
required
The value of the Tab.

Tabs.Indicator

string | ((state: TabsIndicatorState) => string | undefined)
CSS class applied to the element, or a function that returns a class based on the component’s state.
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.Accepts a ReactElement or a function that returns the element to render.
boolean
default:"false"
Whether to render itself before React hydrates. This minimizes the time that the indicator isn’t visible after server-side rendering.
CSSProperties | ((state: TabsIndicatorState) => CSSProperties | undefined)
Style applied to the element, or a function that returns a style object based on the component’s state.

Tabs.Panel

string | ((state: TabsPanelState) => string | undefined)
CSS class applied to the element, or a function that returns a class based on the component’s state.
boolean
default:"false"
Whether to keep the HTML element in the DOM while the panel is hidden.
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.Accepts a ReactElement or a function that returns the element to render.
CSSProperties | ((state: TabsPanelState) => CSSProperties | undefined)
Style applied to the element, or a function that returns a style object based on the component’s state.
any
required
The value of the TabPanel. It will be shown when the Tab with the corresponding value is active.

Migration

Add an explicit Tabs.Indicator to lists that should retain the active underline. Remove duplicate indicators from compositions that previously relied on List inserting one. Keep route links, create controls, and overflow controls outside Tabs.List, whose children select related panels.