Tabs
Switches between views of the same content. A segmented control for short sets and a line variant for page sections.
npx prodular-ui add tabsSegmented
The default. A muted track with the active tab raised on a card surface.
- Requests
- 7.62M
- Error rate
- 0.34%
- p95 latency
- 236 ms
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
const ranges = { "7d": { requests: "1.84M", errors: "0.21%", p95: "212 ms" }, "30d": { requests: "7.62M", errors: "0.34%", p95: "236 ms" }, "90d": { requests: "21.9M", errors: "0.29%", p95: "228 ms" },}
export default function TabsSegmented() { return ( <Tabs defaultValue="30d" className="w-full max-w-md"> <TabsList> <TabsTrigger value="7d">7 days</TabsTrigger> <TabsTrigger value="30d">30 days</TabsTrigger> <TabsTrigger value="90d">90 days</TabsTrigger> </TabsList> {Object.entries(ranges).map(([value, r]) => ( <TabsContent key={value} value={value}> <dl className="grid grid-cols-3 divide-x"> {[ ["Requests", r.requests], ["Error rate", r.errors], ["p95 latency", r.p95], ].map(([label, v]) => ( <div key={label} className="flex flex-col gap-1 px-4 first:pl-0"> <dt className="text-[13px] text-muted-foreground">{label}</dt> <dd className="text-xl font-semibold tracking-tight tabular-nums">{v}</dd> </div> ))} </dl> </TabsContent> ))} </Tabs> )}Line
A 2px primary indicator on a hairline. Use it for sections of a page.
18 people in Halden Analytics. 4 admins, 14 members.
import { Badge } from "@/components/ui/badge"import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
export default function TabsLine() { return ( <Tabs defaultValue="members" className="w-full max-w-lg"> <TabsList variant="line"> <TabsTrigger value="members"> Members <Badge size="sm" variant="secondary"> 18 </Badge> </TabsTrigger> <TabsTrigger value="invites"> Invites <Badge size="sm" variant="secondary"> 3 </Badge> </TabsTrigger> <TabsTrigger value="roles">Roles</TabsTrigger> <TabsTrigger value="audit" disabled> Audit log </TabsTrigger> </TabsList> <TabsContent value="members" className="text-muted-foreground"> 18 people in Halden Analytics. 4 admins, 14 members. </TabsContent> <TabsContent value="invites" className="text-muted-foreground"> 3 pending invites. They expire 7 days after they are sent. </TabsContent> <TabsContent value="roles" className="text-muted-foreground"> Admin, Member and Billing. Custom roles are on the Enterprise plan. </TabsContent> </Tabs> )}Vertical
For settings pages with more than five sections.
Profile
Name, avatar and the email people see on comments.
import { BellIcon, CreditCardIcon, KeyRoundIcon, UserIcon } from "lucide-react"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
const sections = [ { value: "profile", label: "Profile", icon: UserIcon, body: "Name, avatar and the email people see on comments." }, { value: "notifications", label: "Notifications", icon: BellIcon, body: "Choose which events send an email and which stay in the inbox." }, { value: "billing", label: "Billing", icon: CreditCardIcon, body: "Plan, payment method and invoices for the workspace." }, { value: "api", label: "API keys", icon: KeyRoundIcon, body: "Create, rotate and revoke keys. Keys are shown once." },]
export default function TabsVertical() { return ( <Tabs defaultValue="profile" orientation="vertical" className="w-full max-w-lg gap-6"> <TabsList variant="line"> {sections.map(({ value, label, icon: Icon }) => ( <TabsTrigger key={value} value={value}> <Icon data-icon="inline-start" /> {label} </TabsTrigger> ))} </TabsList> {sections.map((s) => ( <TabsContent key={s.value} value={s.value} className="flex flex-col gap-1 pt-1.5"> <h3 className="font-medium">{s.label}</h3> <p className="text-muted-foreground">{s.body}</p> </TabsContent> ))} </Tabs> )}Anatomy
<Tabs defaultValue="members"> <TabsList variant="line"> <TabsTrigger value="members">Members</TabsTrigger> <TabsTrigger value="invites">Invites</TabsTrigger> </TabsList> <TabsContent value="members">...</TabsContent> <TabsContent value="invites">...</TabsContent></Tabs>Guidance
Use it when
- Segmented (default) for two to four short options that filter one view: time ranges, chart modes.
- Line for sections of a page or a panel: Members, Invites, Roles.
- Vertical line tabs for settings with many sections.
Avoid
- Tabs for steps in a sequence. Use a stepper or a multi step form.
- More than six tabs in one row. Group them or use a vertical list.
- Segmented tabs with long labels or icons only.
Rules for AI agents
Published in llms-full.txt, the registry and the MCP server, so coding agents follow them when they build with this component.
- Give every TabsTrigger a value that matches one TabsContent, and set defaultValue on Tabs.
- Use variant="line" on TabsList for page level navigation, the default variant for in-card filters.
- Mark icons inside triggers with data-icon="inline-start".
- Put counts in a Badge size="sm" variant="secondary" inside the trigger, after the label.
- Pass orientation="vertical" on Tabs, not on TabsList.
- Keep labels to one or two words in sentence case.
- Use width="fit" for content-width tabs and size="sm" in dense panels instead of overriding flex-1 and heights on TabsTrigger.
API
Tabs
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultValue | string | The tab that is open on first render. | |
| orientation | "horizontal" | "vertical" | "horizontal" | Lays the list out in a row or a column. |
TabsList
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "line" | "default" | Segmented track or line with an indicator. |
| width | "fill" | "fit" | "fill" | fill stretches segmented tabs across the list; fit sizes each tab to its label. |
| size | "default" | "sm" | "default" | sm for toolbars, inspectors and dense panels. |
TabsTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | Identifies the tab. Required. | |
| disabled | boolean | false | Shows the tab but blocks selection. |
Installation
The Prodular UI CLI adds the source to components/ui with everything it depends on. In a project that already uses shadcn/ui, the shadcn CLI installs it from the Prodular registry too. See Installation for the first setup.
npx prodular-ui add tabs# or, with the registry in components.jsonnpx shadcn@latest add @prodular/tabs