Skip to content

Documentation

neelam-ui

Search documentation

Getting Started
Forms
Overlays
Navigation
Data Display
Layout
AI & Chat
Blocks
GitHub repository

Tabs

A set of panels, one shown at a time, switched between via a strip of tabs — with arrow keys moving focus and selection together, as the WAI-ARIA Tabs pattern specifies.

Update your display name and the email you sign in with.

Usage#

import { Tabs, TabsContent, TabsList, TabsTrigger } from "neelam-ui";
 
<Tabs defaultValue="account">
  <TabsList>
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="password">Password</TabsTrigger>
  </TabsList>
  <TabsContent value="account">Update your display name.</TabsContent>
  <TabsContent value="password">Change your password.</TabsContent>
</Tabs>

Each TabsContent is matched to its TabsTrigger by value.

Orientation#

orientation="vertical" stacks the strip beside the panels. The arrow keys follow: Up/Down replace Left/Right, and aria-orientation is set to match, so assistive tech announces the right ones.

Workspace name, language, and timezone.

Automatic activation#

Arrow keys move focus and selection together — "automatic activation" in the pattern's terms. Arrowing along the strip switches panels as you go, the way a browser's own tab strip behaves.

That differs from the roving focus in Select or DropdownMenu, where arrows only move focus and a separate Enter confirms. The distinction is deliberate: a menu item might be destructive or hard to undo, so it should not fire just because you moved through it. Switching a tab is cheap and reversible.

Automatic activation assumes cheap panels

If a panel triggers an expensive fetch on mount, arrowing across five tabs fires five requests. Cache or debounce the load rather than changing the interaction — the keyboard behaviour here is what the pattern expects.

Inactive panels are unmounted#

Only the active TabsContent is rendered at all. Unlike Accordion, which keeps closed content mounted so it can animate, switching tabs is not animated, so there is nothing to gain from keeping inactive panels around.

The trade-off is real and worth stating plainly: a panel's internal state — a scroll position, an uncontrolled input's value — is lost when you switch away and will not be there when you switch back. Lift anything that must survive into your own state.

Controlled#

const [tab, setTab] = useState("account");
 
<Tabs value={tab} onValueChange={setTab}>…</Tabs>

Keyboard#

Keyboard shortcuts
KeyBehaviour
TabMoves focus to the active tab, then past the whole strip to the panel — not through every tab.
Moves to the previous or next tab, wrapping at the ends. Horizontal orientation.
The same, when orientation="vertical".
HomeEndJumps to the first or last tab.
EnterSpaceActivates the focused tab — though arrowing has already selected it.

Accessibility#

Implements the APG tabs pattern:

  • The strip is role="tablist" with aria-orientation; each tab is a native <button> recategorized as role="tab" with aria-selected and aria-controls pointing at its panel.
  • Roving tabindex: only the active tab is in the page's tab order (tabIndex={0}), the rest are -1. Tab therefore steps past the strip rather than through every tab in it.
  • Each panel is role="tabpanel" with tabIndex={0}, so a keyboard user can move from the tab straight into the panel even when its content has nothing focusable in it.
  • Disabled tabs are skipped by the arrow keys rather than trapping focus.

API reference#

Tabs#

Props for Tabs
PropTypeDefault
defaultValuestring
onValueChange((value: string) => void)
orientation

Defaults to `"horizontal"`.

Orientation"horizontal"
value

Controls which tab is active. Omit to let the tabs manage their own state.

string

TabsList#

Props for TabsList
PropTypeDefault
defaultValuestring
onValueChange((value: string) => void)
orientation

Defaults to `"horizontal"`.

enumhorizontal
value

Controls which tab is active. Omit to let the tabs manage their own state.

string

TabsTrigger#

Props for TabsTrigger
PropTypeDefault
valuerequiredstring

TabsContent#

Props for TabsContent
PropTypeDefault
valuerequiredstring