Tabs
Tabbed navigation between related views with an animated active indicator.
Import
import { Tabs } from '@oztix/roadie-components/tabs'
Examples
Default
Emphasis
emphasis controls the visual treatment of the list and the indicator. The active indicator animates between tabs in every variant — only the appearance of the indicator changes.
strong
A high-contrast segmented control with a solid inverted pill behind the active tab. Use when tabs need to stand out against a busy or photographic background.
normal (default)
A segmented control with a raised pill that slides between active tabs. Use inside cards and toolbars.
subtle
A ghost segmented control — no track, just a tinted pill that slides behind the active tab. Use when the surrounding surface already provides framing.
subtler
A flat row with a sliding underline. Use for in-page section navigation where tabs should defer to the surrounding content.
Sizes
Intents
intent cascades down to descendants via CSS custom properties. The active indicator picks up the intent's strong colour in subtler emphasis.
States
Disabled tabs are skipped during keyboard navigation and ignore clicks.
Composition
With icons
Use the Base UI render prop to access the active state and switch a Phosphor icon's weight between bold and fill.
Vertical direction
Pass direction='vertical' to render a column of left-aligned tabs. With emphasis='subtler' the active indicator becomes a 2px bar on the left edge that slides between tabs; the pill emphases (strong, normal, subtle) keep their full-rect shape around the active tab in vertical mode.
Persistent panels
By default, panels mount only when active. Pass keepMounted on Tabs.Panel to preserve in-flight state (forms, scroll position, video playback) when switching tabs.
Guidelines
- Use Tabs for related views, not for top-level page navigation. Reach for
Breadcrumbor a sidebar instead when tabs would otherwise represent separate pages. - Prefer
emphasis='normal'inside cards or toolbars — the segmented track gives the control its own visual frame. Reach foremphasis='strong'when tabs need to stand out against a busy or photographic background,emphasis='subtle'for a quieter ghost-segmented look, andemphasis='subtler'for a flat underline-only row that defers to the surrounding content. - Place the
Tabs.IndicatorinsideTabs.Listas a sibling of the tabs themselves, not inside a tab. The indicator is positioned absolutely against the list and reads its geometry from Base UI's--active-tab-*CSS variables.
Accessibility
- Keyboard:
Arrowkeys move focus between tabs (ArrowLeft/ArrowRightfor horizontal,ArrowUp/ArrowDownfor vertical).HomeandEndjump to the first and last tab.EnterandSpaceactivate the focused tab. - Active-on-focus: Pass
activateOnFocusonTabs.Listto switch panels as focus moves, instead of waiting forEnter/Space. - ARIA: Base UI handles
role='tablist',role='tab',role='tabpanel',aria-controls,aria-selected, andaria-orientationautomatically. - Reduced motion: The indicator's slide animation is disabled under
prefers-reduced-motion: reduce— the indicator still updates position, just without the transition.
API reference
Tabs
The default value. Use when the component is not controlled. When the value is `null`, no Tab will be active.
Defaults to 0.
The value of the currently active `Tab`. Use when the component is controlled. When the value is `null`, no Tab will be active.
Callback invoked when new value is being set.
Sets the intent cascade for descendant tabs.
Visual treatment for the list and indicator. See the emphasis ladder comment in `variants.ts` for the full description of each option.
Defaults to 'normal'.
Tab height and typography size.
Defaults to 'md'.
Layout flow direction. `vertical` stacks the tabs in a column. The active indicator follows the axis: in `subtler` emphasis the underline becomes a left-edge bar, while the pill emphases (`strong` / `normal` / `subtle`) keep their full-rect shape around the active tab.
Defaults to 'horizontal'.
Tabs.Indicator
Inherited from TabsIndicatorProps
Whether to render itself before React hydrates. This minimizes the time that the indicator isn’t visible after server-side rendering.
Defaults to true.
Tabs.List
Inherited from TabsListProps
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.
Defaults to false.
Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys.
Defaults to true.
Tabs.Panel
Inherited from TabsPanelProps
The value of the TabPanel. It will be shown when the Tab with the corresponding value is active.
Whether to keep the HTML element in the DOM while the panel is hidden.
Defaults to false.
Tabs.Tab
Render the tab as a routed anchor instead of a `<button>`. Useful for tabbed navigation where each tab maps to a route. Internal hrefs route through `RoadieLinkProvider`. Note: pressing Enter on a focused link-tab triggers native browser navigation immediately. For controlled `Tabs.value`, derive the value from the route (e.g. `usePathname()`) rather than from controlled local state — otherwise the active-tab indicator can flicker between selection and route change.
Force external-link treatment when `href` is set.
Override the auto `target='_blank'` default on external hrefs.
Inherited from TabsTabProps
The value of the Tab.
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.
Inherited from NativeButtonProps
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 (e.g. `<div>`).
Defaults to true.