Tabs

Tabs organize content across different screens and views.

Usage

Tabs organizes navigation across views. It is one component family split across a few pieces that only make sense together: Tab is a single tab, TabGroup shares selection between a tab list and its content, and TabPanels/TabPanel render the content for the selected tab. Only Tab children are rendered; other children are ignored.

bash
import { Tab, Tabs } from "@udixio/ui-react"

disabled removes a tab from the roving tab order and blocks selection.

Variants

variant is primary (default, icon above the label, with a thicker 3px indicator) or secondary (icon beside the label, with a thinner 2px indicator).

Connecting tabs to panels

Wrap Tabs and TabPanels in a TabGroup to connect them; this lets you place the tab list and its panels anywhere in the layout, and drives the panel’s slide animation. Tabs alone (no TabGroup) is enough for a navigation-only tab list with no panels, as in the example above.

bash
import { TabGroup, Tabs, Tab, TabPanels, TabPanel } from "@udixio/ui-react"

Profile

This is the profile panel content.

Only the active TabPanel is mounted by the React and Svelte adapters; on Angular, every projected udx-tab-panel keeps its component instance and state alive and the inactive ones are hidden with the native hidden attribute instead — Angular content projection has no cheap equivalent to conditionally mounting projected children. All three remove inactive panels from the accessibility tree and the tab order identically.

Controlled selection and scrollable

Use selectedTab/onSelectedTabChange (React), selectedTab/ selectedTabChange (Angular), or bind:selectedTab (Svelte) for controlled selection on Tabs or TabGroup; defaultSelectedTab (defaults to 0) seeds the initial selection when uncontrolled. Set scrollable to true to enable horizontal scroll with automatic centering of the selected tab.

Do not bind selectedTab directly on Tabs when it is also wrapped in a TabGroup — the TabGroup is the single source of truth in that case, and the two owners would fight over the selection.

If the href prop is provided on a Tab, it renders an <a> link; otherwise it renders a <button>. label can come from a string child as an alternative to the label prop.

jsx
<Tabs>
  <Tab label="Dashboard" />
  <Tab label="Documentation" href="/docs" />
  <Tab label="External" href="https://udixio.dev" />
</Tabs>

Motion

The sliding tab indicator and the panel’s enter transition are implemented once with Motion JavaScript in @udixio/core/dom (createTabsIndicatorController, animateTabPanelEnter), so React, Angular, and Svelte share the same timing and reduced-motion behavior — neither adapter implements its own version of either effect.

Accessibility

Tabs renders role="tablist"; each Tab renders role="tab" with a roving tabIndex (0 on the selected tab, or the first enabled tab when nothing is selected, -1 on the rest). ArrowLeft/ArrowRight move focus and selection between enabled tabs and wrap at the ends; Home/End jump to the first/last enabled tab. When a TabGroup connects a TabPanels, each Tab exposes id/aria-controls matched to its TabPanel’s id/ aria-labelledby, and the panel is in the tab order (tabIndex={0}) so keyboard users can move focus into its content.

Migrating to 3.0

Before3.0
Tab selected (standalone, per-tab)Tabs/TabGroup defaultSelectedTab (single source of truth)
Tabs/TabGroup setSelectedTab (raw dispatcher)onSelectedTabChange (React), selectedTabChange (Angular)
TabGroup defaultTabdefaultSelectedTab
No keyboard navigationRoving tabIndex, ArrowLeft/ArrowRight/Home/End
No disabled on Tabdisabled input/prop
No Angular adapterFull Angular parity (udx-tabs, udx-tab, udx-tab-group, udx-tab-panels, udx-tab-panel)