Skip to Content

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)

PropTypeDefaultDescription
value / defaultValuestringSelected tab
onValueChange(value: string) => void
orientation"horizontal" | "vertical""horizontal"
activationMode"automatic" | "manual""automatic"Automatic activates on focus; manual requires Enter/Space

Tabs.List

PropTypeDefaultDescription
loopbooleantrueWrap focus when reaching the end

Accessibility

  • role="tablist", role="tab", role="tabpanel"
  • aria-controls / aria-labelledby bidirectional
  • 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