Components

Tabs

A React tabs component with a Liquid Glass tab list, animated active indicator, and keyboard navigation.

Tabs switches between related panels on the same page. The tab list sits on a shared glass surface, and a translucent lens follows the active tab.

Tabs are passed through the items array. Each item's value links its tab button to the matching panel, while content renders inside that panel. Use defaultValue for uncontrolled usage or value with onValueChange for a controlled component.

Basic example

Labels and icons share the tab button. The first enabled tab is active by default.

Profile

Update your name, photo, and contact details.

Sizes

Supports xs / sm / md / lg / xl, defaulting to md. Tab height, padding, text, icons, spacing, and panel text scale together.

xs
Workspace summary and recent activity.
sm
Workspace summary and recent activity.
md
Workspace summary and recent activity.
lg
Workspace summary and recent activity.
xl
Workspace summary and recent activity.

Controlled usage

value and onValueChange let external state fully control the active tab. Set value to null when no tab should be active.

Showing the overview panel.

Active tab: Overview

Vertical orientation

Use orientation="vertical" for a side-by-side layout. Arrow keys follow the orientation, so ArrowDown and ArrowUp move through vertical tabs.

General

Project name, description, and visibility.

Disabled tabs

Set disabled on the component to disable every tab, or on a single item to disable only that tab. Disabled tabs stay visible and remain part of the tab list.

Disabled group
Workspace summary.
One disabled tab
Workspace summary.

Persistent panels

Panels unmount when another tab becomes active. Set keepMounted on an item to keep its panel in the DOM so local state survives tab switches.

This panel unmounts when another tab becomes active, so the field is cleared.

Activation on focus

By default, arrow keys move focus and Enter or Space activates the tab. Set activateOnFocus to switch panels as focus moves.

Showing the overview panel.

Active tab: Overview. Focus the list and press an arrow key to switch without pressing Enter.

Icon-only tabs

Tabs are named by their label. When a tab renders only an icon, give it an aria-label or aria-labelledby.

Update your name, photo, and contact details.

Accessibility

Tabs renders a tablist of tab buttons and linked tabpanels. The active tab exposes aria-selected, and each panel is connected with aria-controls and aria-labelledby.

Arrow keys move focus along the orientation, Home and End jump to the ends, and Enter or Space activates the focused tab when activateOnFocus is false. Name the tab list with aria-label or aria-labelledby; icon-only tabs need their own name. See the accessibility guide for keyboard details.

API reference

Tabs

PropTypeDefaultDescription
itemsreadonly TabsItemProps[]—Tabs and panels rendered by the component.
valueany—Controlled active value. Use null to render no active tab.
defaultValueanyFirst enabled itemInitial active value when uncontrolled.
onValueChange(value: any, details) => void—Called when the active value changes.
orientation"horizontal" | "vertical""horizontal"Layout and keyboard orientation.
activateOnFocusbooleanfalseActivates a tab as arrow-key focus moves.
loopFocusbooleantrueLoops keyboard focus back to the first tab after the last one.
disabledbooleanfalseDisables every tab.
size"xs" | "sm" | "md" | "lg" | "xl""md"Tab height, padding, text, icons, and spacing.
aria-labelstring—Accessible name for the tab list.
aria-labelledbystring—ID of an external element that names the tab list.
aria-describedbystring—ID of external text that describes the tab list.
renderReactElement | ((props, state) => ReactElement)—Replaces the root element.
refRef<HTMLDivElement>—Ref forwarded to the root element.
classNamestring—Tailwind classes merged into the root.
styleCSSProperties—Inline styles merged into the same element as className.
ui{ root?: ClassValue; list?: ClassValue; glass?: ClassValue; overlay?: ClassValue; indicator?: ClassValue; tab?: ClassValue; icon?: ClassValue; label?: ClassValue; panels?: ClassValue; panel?: ClassValue; panelContent?: ClassValue }—Tailwind classes for internal slots, keyed by data-slot.

TabsItem

PropTypeDefaultDescription
valueany—Value that links the tab to its panel.
labelReactNode—Visible label inside the tab.
iconReactNode—Decorative icon rendered before the label.
contentReactNode—Panel content shown while the tab is active.
keepMountedbooleanfalseKeeps the panel in the DOM while another tab is active.
disabledbooleanfalseDisables this tab.
aria-labelstring—Accessible name for an icon-only tab.
aria-labelledbystring—Names an icon-only tab from another element.
nativeButtonbooleantrueWhether the tab renders a native <button>.
renderReactElement | ((props, state) => ReactElement)—Replaces the tab element.
refRef<HTMLElement>—Ref forwarded to the tab element.
classNamestring—Tailwind classes merged into the tab button. Use ui.panel for the panel.
styleCSSProperties—Inline styles merged into the same element as className.
ui{ tab?: ClassValue; icon?: ClassValue; label?: ClassValue; panel?: ClassValue; panelContent?: ClassValue }—Tailwind classes for this tab's internal slots, keyed by data-slot.

Root ui is merged into every tab and panel first, then the item's ui. The item's className applies only to the tab button; style the panel through ui.panel.

AttributeDescription
data-activePresent on the active tab.
data-disabledPresent when the component or tab is disabled.
data-orientationIndicates the orientation (horizontal or vertical).
data-activation-directionIndicates the direction of the newly active tab relative to the previous one.
data-hiddenPresent on a mounted panel that is not active.
data-starting-stylePresent when a panel begins animating in.
data-ending-stylePresent when a panel is animating out.
data-slotMarks internal parts (root, list, glass, overlay, indicator, tab, icon, label, panels, panel, panel-content).

Theming

The component uses the shared glass material. Tune shared --ui-glass-* tokens on a parent, use className for layout, and use ui for internal slots keyed by data-slot. Component-specific values live in src/theme/tabs.ts. See the theming guide for token layers and presets.

  • Accordion — For stacked disclosures without a tab list.
  • Toggle Group — For a compact view switch that is not content navigation.
  • Scroll Area — When a panel needs its own scroll container.