Tabs
Client leafSwitch between related panels.
Live preview
Rendered from the installed VEXA source — the same files you receive.
A summary of your workspace and recent usage.
Installation
Adds the component source to your project. Registry dependencies are pulled in automatically.
npx shadcn@latest add https://vexa.valfiguer.com/r/tabs.jsonInstalls to components/vexa/tabs.tsx
npm dependencies
- @radix-ui/react-tabs
Registry dependencies
- @vexa/utils
Other VEXA items this component needs. Install them first — they are resolved automatically by the CLI.
Source
Copy this into its target path, or install it with the command above.
components/vexa/tabs.tsx
"use client";
import type * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cn } from "@/lib/utils";
/**
* Tabs — switch between related panels.
*
* Client leaf. Radix implements the APG tabs pattern: roving `Tab`/`Shift+Tab`
* focus onto the active trigger, Arrow keys to move between triggers (Home/End
* to jump), and `tab`/`tabpanel` roles with `aria-selected`/`aria-controls`
* wired up. Automatic activation is the default; set `activationMode="manual"`
* for panels whose content is expensive to render.
*/
export function Tabs({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
className={cn("flex flex-col gap-3", className)}
{...props}
/>
);
}
export function TabsList({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.List>) {
return (
<TabsPrimitive.List
data-slot="tabs-list"
className={cn(
"inline-flex h-10 w-fit items-center justify-center gap-1 rounded-vexa-md bg-vexa-muted p-1 text-vexa-muted-foreground",
className,
)}
{...props}
/>
);
}
export function TabsTrigger({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
<TabsPrimitive.Trigger
data-slot="tabs-trigger"
className={cn(
"inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-vexa-sm px-3 py-1.5 text-sm font-medium",
"transition-[color,background-color,box-shadow] duration-[var(--vexa-duration-fast)] ease-[var(--vexa-ease-standard)]",
"outline-none focus-visible:ring-2 focus-visible:ring-vexa-ring focus-visible:ring-offset-2 focus-visible:ring-offset-vexa-background",
"disabled:pointer-events-none disabled:opacity-50",
"data-[state=active]:bg-vexa-surface-raised data-[state=active]:text-vexa-foreground data-[state=active]:shadow-vexa-sm",
"[&_svg]:size-4 [&_svg]:shrink-0",
className,
)}
{...props}
/>
);
}
export function TabsContent({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
return (
<TabsPrimitive.Content
data-slot="tabs-content"
className={cn(
"outline-none focus-visible:ring-2 focus-visible:ring-vexa-ring focus-visible:ring-offset-2 focus-visible:ring-offset-vexa-background",
className,
)}
{...props}
/>
);
}
Details
Example
tsx
<Tabs defaultValue="a"><TabsList><TabsTrigger value="a">Account</TabsTrigger></TabsList><TabsContent value="a">...</TabsContent></Tabs>Accessibility
APG tabs: roving Tab focus onto the active trigger, Arrow/Home/End to move between triggers, and tab/tabpanel roles with aria-selected/aria-controls.