Tabs
A keyboard-navigable set of layered content panels.
- Chromium
- Firefox
- WebKit
- WCAG 2.2 AA
Preview
Account
Password
Account
Make changes to your account here. Click save when you’re done.
import { Button } from "@moe-ui/registry/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@moe-ui/registry/components/ui/card";
import { Input } from "@moe-ui/registry/components/ui/input";
import { Label } from "@moe-ui/registry/components/ui/label";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@moe-ui/registry/components/ui/tabs";
import { Text } from "@moe-ui/registry/components/ui/text";
import { View } from "react-native";
export default function TabsPreview() {
return (
<Tabs defaultValue="account" className="w-[400px]">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="account">
<Text>Account</Text>
</TabsTrigger>
<TabsTrigger value="password">
<Text>Password</Text>
</TabsTrigger>
</TabsList>
<TabsContent value="account">
<Card>
<CardHeader>
<CardTitle>Account</CardTitle>
<CardDescription>
Make changes to your account here. Click save when you’re done.
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<View className="space-y-1">
<Label htmlFor="name">Name</Label>
<Input id="name" defaultValue="Pedro Duarte" />
</View>
<View className="space-y-1">
<Label htmlFor="username">Username</Label>
<Input id="username" defaultValue="@peduarte" />
</View>
</CardContent>
<CardFooter>
<Button>
<Text>Save changes</Text>
</Button>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="password">
<Card>
<CardHeader>
<CardTitle>Password</CardTitle>
<CardDescription>
Change your password here. After saving, you’ll be logged out.
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<View className="space-y-1">
<Label htmlFor="current">Current password</Label>
<Input id="current" secureTextEntry />
</View>
<View className="space-y-1">
<Label htmlFor="new">New password</Label>
<Input id="new" secureTextEntry />
</View>
</CardContent>
<CardFooter>
<Button>
<Text>Save password</Text>
</Button>
</CardFooter>
</Card>
</TabsContent>
</Tabs>
);
}
One command, your source
Installation
pnpm dlx @moe-ui/cli@beta add tabsUsage
The CLI installs editable source into your application. Import the component from your configured UI directory:
import { Tabs } from "@/components/ui/tabs";Variants and composition
Compound parts and variants remain regular source in your project, so you can change them without wrapping a package API.
Public API
The canonical source panel below lists every public export, dependency, and the complete implementation shipped by the registry.
Keyboard and accessibility
Arrow-key and Tab behavior follows the underlying accessible primitive. Focus indicators remain visible in both themes.
Browser support
This beta is release-tested in Chromium, Firefox, and WebKit in light and dark themes. See the web compatibility matrix for the current gate.
Nothing hiddenCanonical source and public exports4 exports
TabsTabsContentTabsListTabsTrigger"use client";
import { TextClassContext } from "./text";
import { cn } from "../../lib/utils";
import * as TabsPrimitive from "@rn-primitives/tabs";
import * as React from "react";
import { Platform } from "react-native";
function Tabs({
className,
value: valueProp,
defaultValue,
onValueChange,
...props
}: Omit<TabsPrimitive.RootProps, "value" | "onValueChange"> &
React.RefAttributes<TabsPrimitive.RootRef> & {
value?: string;
defaultValue?: string;
onValueChange?: (value: string) => void;
}) {
const [uncontrolledValue, setUncontrolledValue] = React.useState(
defaultValue ?? "",
);
const value = valueProp ?? uncontrolledValue;
return (
<TabsPrimitive.Root
className={cn("flex flex-col gap-2", className)}
value={value}
onValueChange={(nextValue) => {
if (valueProp === undefined) setUncontrolledValue(nextValue);
onValueChange?.(nextValue);
}}
{...props}
/>
);
}
function TabsList({
className,
...props
}: TabsPrimitive.ListProps & React.RefAttributes<TabsPrimitive.ListRef>) {
return (
<TabsPrimitive.List
className={cn(
"bg-muted flex h-9 flex-row items-center justify-center rounded-lg p-[3px]",
Platform.select({ web: "inline-flex w-fit", native: "mr-auto" }),
className,
)}
{...props}
/>
);
}
function TabsTrigger({
className,
...props
}: TabsPrimitive.TriggerProps & React.RefAttributes<TabsPrimitive.TriggerRef>) {
const { value } = TabsPrimitive.useRootContext();
return (
<TextClassContext.Provider
value={cn(
"text-foreground dark:text-muted-foreground text-sm font-medium",
value === props.value && "dark:text-foreground",
)}
>
<TabsPrimitive.Trigger
className={cn(
"flex flex-row items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 shadow-none shadow-black/5",
Platform.select({
web: "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring web:h-[calc(100%-1px)] inline-flex cursor-default whitespace-nowrap transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0",
}),
props.disabled && "opacity-50",
props.value === value &&
"bg-background dark:border-foreground/10 dark:bg-input/30",
className,
)}
{...props}
/>
</TextClassContext.Provider>
);
}
function TabsContent({
className,
...props
}: TabsPrimitive.ContentProps & React.RefAttributes<TabsPrimitive.ContentRef>) {
return (
<TabsPrimitive.Content
className={cn(Platform.select({ web: "flex-1 outline-none" }), className)}
{...props}
/>
);
}
export { Tabs, TabsContent, TabsList, TabsTrigger };