Tabs
Switch between a few peer views in place, showing one panel at a time.
@garn/ui/tabsDefault
Two-tab Account / Password switcher.
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add tabs- Registry
tabs- Source
- tabs.tsx
- Deps
@radix-ui/react-tabslucide-react- Registry deps
utilscounter
Import
import { Tabs } from "@garn/ui/tabs";Props
Generated from the component's TypeScript types.
mount- Description
- Panel mounting. `active` (default) mounts a panel's children on first activation; `eager` renders every panel up front (today's behavior — SEO / measure-on-mount). Per-panel `keepMounted` persists an activated panel.
- Type
TabsMountMode
size- Description
- No description.
- Type
TabsSize
variant- Description
- Visual language: `underline` (default), `pill` (segmented), `enclosed` (card).
- Type
TabsVariant
Plus 285 inherited native HTML attributes.
TabsList
indicator- Description
- Render an animated active indicator (default true). Set false to opt out.
- Type
boolean
scrollButtons- Description
- Show prev/next scroll buttons flanking the list when it overflows (the pointer affordance — keyboard + trackpad scroll already work). The buttons sit *outside* the tablist so the ARIA structure stays clean; they fade out at the reached end. Compose `TabsScrollButton` by hand for bespoke layouts.
- Type
boolean
Plus 282 inherited native HTML attributes.
TabsTrigger
badge- Description
- Trailing badge. A number renders a garn `Counter` matched to the tab's size (e.g. an unread count); a node renders verbatim.
- Type
React.ReactNode
icon- Description
- Leading icon (terse path). Compose children for full control.
- Type
React.ReactNode
Plus 291 inherited native HTML attributes.
TabsContent
keepMounted- Description
- Keep this panel mounted (hidden) once it has been activated, so its form / scroll state survives a tab switch. Overrides the root's `mount="active"` lazy unmount for this panel only.
- Type
boolean
Plus 283 inherited native HTML attributes.
Styling
Target these data-slots and remap these tokens to restyle without forking the component.
[data-slot="tabs"]| Property | Token | Tier |
|---|---|---|
listHeight | --garn-control-h-md | semantic · air |
ring | --garn-ringbrandable | semantic |
Overview
Tabs switch between a few peer views in the same context, one panel at a time — settings sections, overview/detail, related lists. Tabs owns the active value and orientation, TabsList holds the TabsTriggers (roving focus), and each TabsContent panel shows when its value is active. Use tabs when the views are siblings the user toggles between; for sequential, gated steps use a Stepper, and for primary site navigation use NavigationMenu.
Guidelines
Tabs are for peer views, not steps or pages. They imply equal, independent sections in one place — don't use them for an ordered flow (that's a Stepper) or to navigate to separate URLs (that's navigation). Keep the set small enough to see at once; many tabs that wrap or scroll signal the content wants a different structure.
Use short, parallel labels and a sensible default. One or two words each, in the same grammatical form, with the most important panel active first. Don't bury a required action inside a non-default tab where users may never look.
Respect the keyboard model. Arrow keys move between triggers and the active panel is wired via aria-controls; don't put the only path to critical content behind a tab a keyboard user might skip. Avoid tabs within tabs.
Best practices
- Give each TabsTrigger a
valueand a matching TabsContent. - Keep labels short so the tablist fits on one line.
- Set
defaultValue(uncontrolled) orvalue+onValueChange(controlled).
- Don't use tabs for a linear, ordered flow.
- Don't bury content the user always needs behind a non-default tab.
- Don't overload the tablist — switch to a nav menu or accordion when there are many sections.
Content guidelines
- Use short, parallel tab labels (one or two words) that name each view.
- Order tabs by importance or natural sequence, and keep the default tab first.
Accessibility
- Role
tablist- ARIA APG
- tabs
- Focus
- Triggers and the active panel show a 2px ring; focus moves list → panel on Tab.
Keyboard
| ArrowLeftArrowRight | Move between tabs (horizontal orientation). |
| ArrowUpArrowDown | Move between tabs (vertical orientation). |
| HomeEnd | Jump to the first / last tab. |
| Tab | Move focus from the tablist into the active panel. |
| EnterSpace | Activate the focused tab (manual activation mode). |
- • The list is role=tablist with roving tabindex; each trigger is role=tab with aria-selected.
- • Each trigger aria-controls its panel; each panel is role=tabpanel, aria-labelledby its trigger.
- • Default activation is automatic (focus selects). Use `activationMode="manual"` to require Enter/Space.
- Status
- Stable
- Version
v0.3.0- Introduced
v0.1.0
garn doesn't yet record a per-release version history. This reflects the component's current lifecycle state; full changelog entries will appear once the metadata pipeline emits a lifecycle.history field.