Tabs
Tab list with roving tabindex, automatic or manual activation, horizontal or vertical.
Anatomy
import { Tabs } from "@spacing-ui/core";
<Tabs defaultValue="a" orientation="horizontal" activationMode="automatic">
<Tabs.List>
<Tabs.Trigger value="a">A</Tabs.Trigger>
<Tabs.Trigger value="b">B</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="a">Panel A</Tabs.Content>
<Tabs.Content value="b">Panel B</Tabs.Content>
</Tabs>;API
Tabs (Root)
| Prop | Type | Default | Description |
|---|---|---|---|
value / defaultValue | string | — | Selected tab |
onValueChange | (value: string) => void | — | |
orientation | "horizontal" | "vertical" | "horizontal" | |
activationMode | "automatic" | "manual" | "automatic" | Automatic activates on focus; manual requires Enter/Space |
Tabs.List
| Prop | Type | Default | Description |
|---|---|---|---|
loop | boolean | true | Wrap focus when reaching the end |
Accessibility
role="tablist",role="tab",role="tabpanel"aria-controls/aria-labelledbybidirectional- Roving tabindex: only the selected tab is in the tab order
- Keyboard: ArrowRight/Left (horizontal) or ArrowDown/Up (vertical), Home, End
Examples
Manual activation
<Tabs defaultValue="a" activationMode="manual">
<Tabs.List>
<Tabs.Trigger value="a">A</Tabs.Trigger>
<Tabs.Trigger value="b">B</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="a">A</Tabs.Content>
<Tabs.Content value="b">B</Tabs.Content>
</Tabs>Arrow keys move focus without switching the active tab. Enter or Space activates the focused tab.
Lazy-loading panels
Tabs.Content unmounts when its tab is inactive by default. Pair with React.lazy to avoid loading a heavy panel until it’s shown:
const Analytics = React.lazy(() => import("./analytics"));
<Tabs.Content value="analytics">
<Suspense fallback={<Spinner />}>
<Analytics />
</Suspense>
</Tabs.Content>Live demo
Update your account details.
Testing
const tab = screen.getByRole("tab", { name: /password/i });
await userEvent.click(tab);
expect(tab).toHaveAttribute("aria-selected", "true");
expect(screen.getByRole("tabpanel")).toHaveTextContent(/change your password/i);Vertical orientation
<Tabs defaultValue="a" orientation="vertical">
<Tabs.List style={{ display: "flex", flexDirection: "column" }}>...</Tabs.List>
...
</Tabs>Arrow key handling follows orientation automatically. Layout is up to you.
Last updated on