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

Small (sm)
Medium (md)
Large (lg)

Tabs with icons + buttons without icons

Small (sm)
Medium (md)
Large (lg)

Tabs without icons + buttons with icons

Small (sm)
Medium (md)
Large (lg)

Examples

Full Width Tabs

Daily view content

With Content Panels

1,284

Total events this month

Tabs props

PropTypeDefaultDescription
valuestring-The controlled value of the active tab.
defaultValuestring-The default value when uncontrolled.
onValueChange(value: string) => void-Callback when the active tab changes.

TabsList props

PropTypeDefaultDescription
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.
classNamestring-Extra Tailwind classes. Use for layout (e.g. w-full to span the parent); avoid overriding the visual sizing unless intentional.

TabsTrigger props

PropTypeDefaultDescription
valuestring-The value this trigger activates. Must match a TabsContent value.
size"sm" | "md" | "lg"inheritedOverride the size inherited from the parent TabsList. Use sparingly — keeping size on the list keeps the row consistent.
tooltipReact.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.
disabledbooleanfalsePrevent the trigger from being selected.
childrenReact.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.
classNamestring-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-label so 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>
```