Preview
Draft a shorter onboarding flow.
Start with one goal. Ask for workspace details after the first useful result.
Design guidance
Use tabs for peer views of one subject. Use links for navigation to another page. Keep labels short and name the list. Arrow keys move focus; Enter or Space selects. Set activateOnFocus on the list only when switching panels is immediate.
Group width and item width are independent. A full-width group can keep content-sized items, and a content-sized group can give each item the width of the longest label. Vertical items always share a width and align their content to the start.
Installation
Basic usage
Structures
Match each tab's value with its panel's value. Content and selection policy belong to the caller. Tabs is also callable as the root. The default list supplies hover feedback and the selected underline; add Tabs.Indicator only when a custom list render needs Base UI's measured marker.
Examples
Styling
Segmented
Use a contained switcher for a compact group of peer views. SharedBackground moves a card-colored hover surface over the rail; only the selected item has an inset border.
Underline
Keep the selected underline visible while a separate line follows hover or focus. There is no line across the whole list and no gap between items.
Ghost
Use a quiet selected surface without a surrounding rail.
Sizes
All styles use 2px container padding and regular text. Base items are at least 28px high; small items are at least 24px. Their single-row containers are 32px and 28px high, matching base and small Buttons. Touch targets grow to 44px items and 48px containers. Wrapping labels can increase height.
Base
Small
Width
Content items · content group
Both widths follow their content. This is the default.
Equal items · content group
Each item matches the longest label without filling the parent.
Content items · full group
The group fills its parent while items retain their own widths.
Equal items · full group
Items divide the available group width equally. Long labels wrap rather than disappear.
Small screens
Scrollable
Keep labels at their content width and let the list scroll horizontally. The scrollbar stays hidden. Arrow-key focus brings each tab into view; selection still uses Enter or Space.
Move workspace setup after the first useful result.
Select
Below 640px, replace the list with Select. Both controls share one selected value, so the current panel stays open when the viewport changes. The breakpoint belongs to this example, not the Tabs API.
This composition also needs Select.
Move workspace setup after the first useful result.
Orientation
Vertical
Items share the list width, with labels aligned to the start. Arrow Up and Arrow Down move focus. In a full-width vertical layout, the list and panel share available space.
Draft a shorter onboarding flow.
Start with one goal. Ask for workspace details after the first useful result.
Special use case
Timed background / auto-playing tabs
Paint elapsed time directly on the active tab's background. useProgressTimer from Progress supplies elapsed time. The caller advances selection and resets the shared timer with the selected value. This example pauses on hover, focus, a hidden page, or Pause autoplay. Reduced motion turns autoplay off.
This composition also needs Progress and Button.
Move workspace setup after the first useful result.
Autoplay is off because reduced motion is enabled.
Panel content
Panels accept caller-owned content. Give panels a consistent responsive width to prevent the group from moving when their content changes. Keep the visible panel connected to its tab and preserve useful headings, lists, and actions inside the panel.
Move workspace setup after the first useful result.
API reference
Root
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "segmented" | "line" | "ghost" | "segmented" | Appearance only. The line variant is shown as Underline. |
size | "base" | "sm" | "base" | Item typography and minimum height. List padding stays 2px. |
width | "content" | "full" | "content" | Group width. Full expands the root and its list; item distribution remains independent. |
orientation | "horizontal" | "vertical" | "horizontal" | Layout and arrow-key direction. |
value / defaultValue | TabsPrimitive.Tab.Value | null | Controlled selection or initial uncontrolled selection. Null means no active tab. | |
onValueChange | (value, eventDetails) => void | Selection changes, including Base UI automatic fallback when a tab is missing or disabled. | |
className / style / render | Native Base UI root styling and composition props | Root className is a string; style may be an object or state function. Render replaces or composes the root element. |
List
| Prop | Type | Default | Description |
|---|---|---|---|
itemWidth | "content" | "equal" | "content" | Horizontal item widths. Equal uses the longest label in a content group or equal shares in a full group. Vertical items always share the list width. |
activateOnFocus | boolean | false | Select the focused tab on arrow-key navigation. |
aria-label / aria-labelledby | string | Provide an accessible name for the group. | |
className / style / render / ref | Base UI List props | Native attributes, state styling, forwarded ref and custom list composition. A custom render owns its hover/indicator composition. |
Tab
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | TabsPrimitive.Tab.Value | Identity matched by the corresponding panel. | |
disabled | boolean | false | Prevents selection; Base UI manages focus and fallback. |
progress | number | Optional elapsed percent, clamped to 0–100. Only the active enabled tab paints a fill. Omitted or non-finite values show no progress. Does not start a timer. | |
className / style / render / ref | Base UI Tab props | Native attributes, state styling, forwarded ref and supported element composition. |
Panel and Indicator
Tabs.Panel matches a tab by value and accepts children, native attributes, state styling, render, and a forwarded ref. Base UI controls panel mounting and visibility. keepMounted preserves inactive panel DOM when needed.
Tabs.Indicator exposes Base UI's active-tab geometry for custom list renders. The default underline list already draws its selected line and suppresses a duplicate explicit Indicator. Panels, refs, right-to-left direction, focus looping, and selection callbacks retain Base UI Tabs behavior.
Arrow keys move focus among enabled tabs. Enter or Space selects. Changing width, style, or progress does not change these semantics. Autoplay examples need a pause control and must not move keyboard focus when they advance.
useProgressTimer({ active, duration, paused, resetKey, onComplete }) is exported from @/components/ui/progress. It returns elapsed percent (0–100). active defaults to true, duration to 5000ms, and paused to false. Changing resetKey or duration, or reactivating the timer, starts a new interval. Completion runs once per interval; pause preserves elapsed time.