Tabs
A set of layered sections of content that display one panel at a time.
Installation
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@gradeui/ui"Usage
Make changes to your account here.
Sizes
T-shirt sizes: sm, md (default), lg. Set on TabsList; the size cascades to every trigger inside via context, so you set it once.
Icons
Pass an icon as a sibling of the label inside TabsTrigger. The component handles sizing and spacing automatically — any <svg> child is set to size-3.5 (14 px) and laid out with gap-1.5 against the label. No icon prop, no per-call sizing.
Icon and label
Rendered preview goes here.
Icon only
Omit the label entirely for compact icon-only tabs. Pass a tooltip prop and the component wraps the trigger in the design-system Tooltip automatically — the same string is also applied as aria-label when not set explicitly, so screen readers always have an accessible name. Requires a TooltipProvider above the tabs (the design system root layout mounts one app-wide).
Icon-led navigation
A common pattern: every tab carries an icon for fast visual scanning, plus a label for clarity. Pair with the full-width flex-1 trick if you want the strip to fill its container.
Layout controls.
Composing with buttons
Tabs and Button both consume the theme’s --radiustoken, so their corner rounding always matches without per-call overrides. Switch the active theme’s radius (sharp / subtle / round / pill) and both primitives follow.
Toolbar row
A common pattern: a tab strip on the left, action buttons on the right. Pair TabsList size="md" with Button size="sm" — they read as the same row of chrome.
Size pairing reference
T-shirt sizes match EXACTLY in outer height between primitives:TabsList size="sm" = Button size="sm"= 28 px,md= 32 px,lg= 40 px. A button next to a tab strip lines up at every size, with or without icons on either side.
Plain — no icons
Tabs with icons + buttons without icons
Tabs without icons + buttons with icons
Examples
Full Width Tabs
Daily view content
With Content Panels
Total events this month
Tabs props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | The controlled value of the active tab. |
| defaultValue | string | - | The default value when uncontrolled. |
| onValueChange | (value: string) => void | - | Callback when the active tab changes. |
TabsList props
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "md" | T-shirt size for the list and every TabsTrigger inside it (cascades via context). md is the default — compact density that matches the rest of the chrome. sm for dense admin rows, lg for hero / settings pages. |
| className | string | - | Extra Tailwind classes. Use for layout (e.g. w-full to span the parent); avoid overriding the visual sizing unless intentional. |
TabsTrigger props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | The value this trigger activates. Must match a TabsContent value. |
| size | "sm" | "md" | "lg" | inherited | Override the size inherited from the parent TabsList. Use sparingly — keeping size on the list keeps the row consistent. |
| tooltip | React.ReactNode | - | When set, wraps the trigger in the design-system Tooltip primitive. Designed for icon-only triggers — the tooltip text is also applied as aria-label when none is set, so screen readers can name the choice. Requires a TooltipProvider above the tabs. |
| disabled | boolean | false | Prevent the trigger from being selected. |
| children | React.ReactNode | - | Trigger content. Pass an icon as a sibling of the label (e.g. <Icon /> Label) — TabsTrigger auto-sizes any SVG child via [&_svg]:size-* and lays it out with the appropriate gap. No icon prop is needed. |
| className | string | - | Extra Tailwind classes. Use for layout (e.g. flex-1 for full-width tabs); avoid overriding the visual styles unless intentional. |
Accessibility
- Built on Radix UI Tabs for full accessibility
- Keyboard navigation (Arrow keys, Home, End)
- WAI-ARIA tablist pattern
- Tab panels are properly associated with triggers
- For icon-only triggers, always pair with an
aria-labelso the choice has an accessible name
Sidecar
The Markdown sidecar Studio (and the Grade MCP server, when it ships) reads to understand this component — frontmatter, when- to-use guidance, and canonical examples. Authored once at packages/ui/components/ui/tabs.md and shipped inside the published @gradeui/ui tarball.
---
name: Tabs
import: "@gradeui/ui"
subcomponents: [TabsList, TabsTrigger, TabsContent]
sizes: [sm, md, lg]
variants: [pill, underlined]
props:
- Tabs: defaultValue?, value?, onValueChange?, orientation?
- TabsList: size? (sm | md | lg, default md) — t-shirt scale aligned with Button/ToggleGroup heights; cascades to every TabsTrigger via context so set it once on the list
- TabsList: variant? (pill | underlined, default pill) — `pill` is the shadcn chip-on-muted look; `underlined` is the minimal text + bottom-border treatment (formerly the separate SimpleTabs component, collapsed into Tabs in May 2026). Cascades to triggers.
- TabsTrigger: value: string — matches a TabsContent value; tooltip?: string — when set, wraps the trigger in the design-system Tooltip and auto-applies aria-label (useful for icon-only triggers); requires a TooltipProvider somewhere above the tabs
- TabsContent: value: string — matches a TabsTrigger value
when_to_use: A small set of peer views within one surface (2–5 tabs). For primary nav use Side Menu/routing. For filters use a filter control, not tabs. Pick `variant="pill"` for app chrome (settings panels, in-card tab strips). Pick `variant="underlined"` for marketing/docs pages and browser-tab-style treatments.
composes_with: [Card (tabs inside a card body), Dialog, TooltipProvider (required for tooltip prop)]
aliases: [tabs, tab strip, tab bar, tab view, tabbed interface, pageviewcontroller, react native tab view, underlined tabs, page tabs, segment switcher, simple tabs]
---
```jsx
<Tabs defaultValue="details">
<TabsList>
<TabsTrigger value="details">Details</TabsTrigger>
<TabsTrigger value="activity">Activity</TabsTrigger>
</TabsList>
<TabsContent value="details">…</TabsContent>
<TabsContent value="activity">…</TabsContent>
</Tabs>
```
```jsx
// Icon-only triggers — `tooltip` adds the design-system Tooltip + aria-label.
<TooltipProvider>
<Tabs defaultValue="preview">
<TabsList size="sm">
<TabsTrigger value="preview" tooltip="Preview"><Eye /></TabsTrigger>
<TabsTrigger value="code" tooltip="Code"><Code /></TabsTrigger>
</TabsList>
<TabsContent value="preview">…</TabsContent>
<TabsContent value="code">…</TabsContent>
</Tabs>
</TooltipProvider>
```
```jsx
// Underlined variant — replaces the old SimpleTabs component. Use
// `variant="underlined"` on the TabsList and it cascades to triggers.
<Tabs defaultValue="profile">
<TabsList variant="underlined">
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="team">Team</TabsTrigger>
<TabsTrigger value="billing">Billing</TabsTrigger>
</TabsList>
<TabsContent value="profile">…</TabsContent>
<TabsContent value="team">…</TabsContent>
<TabsContent value="billing">…</TabsContent>
</Tabs>
```