Tabs
Tab navigation with animated indicator for switching between content panels.
Account settings content here.
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
export function MyScreen() {
return (
<Tabs defaultValue="account">
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">
<Text>Account settings content here.</Text>
</TabsContent>
<TabsContent value="password">
<Text>Password settings content here.</Text>
</TabsContent>
</Tabs>
);
}Installation
npx @aniui/cli add tabsThis component requires react-native-reanimated for the tab indicator animation.
Usage
app/index.tsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
export function MyScreen() {
return (
<Tabs defaultValue="account">
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">
<Text>Account settings content here.</Text>
</TabsContent>
<TabsContent value="password">
<Text>Password settings content here.</Text>
</TabsContent>
</Tabs>
);
}Compound Components
ComponentDescription
TabsRoot container managing tab state
TabsListContainer for tab triggers
TabsTriggerPressable tab button with animated background
TabsContentContent panel shown when tab is active
Props
Tabs
PropTypeDefault
defaultValuestringrequiredclassNamestring—TabsTrigger
PropTypeDefault
valuestringrequiredclassNamestring—TabsContent
PropTypeDefault
valuestringrequiredclassNamestring—Source
components/ui/tabs.tsx
import React, { createContext, useContext, useState } from "react";
import { View, Pressable, Text } from "react-native";
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
import { cn } from "@/lib/utils";
const TabsCtx = createContext<{ value: string; onValueChange: (v: string) => void }>({ value: "", onValueChange: () => {} });
export interface TabsProps extends React.ComponentPropsWithoutRef<typeof View> {
className?: string;
defaultValue: string;
children?: React.ReactNode;
}
export function Tabs({ defaultValue, className, children, ...props }: TabsProps) {
const [value, setValue] = useState(defaultValue);
return (
<TabsCtx.Provider value={{ value, onValueChange: setValue }}>
<View className={cn("", className)} {...props}>{children}</View>
</TabsCtx.Provider>
);
}
export interface TabsListProps extends React.ComponentPropsWithoutRef<typeof View> {
className?: string;
children?: React.ReactNode;
}
export function TabsList({ className, ...props }: TabsListProps) {
return <View className={cn("flex-row rounded-lg bg-muted p-1", className)} {...props} />;
}
export interface TabsTriggerProps extends React.ComponentPropsWithoutRef<typeof Pressable> {
className?: string;
value: string;
children: React.ReactNode;
}
export function TabsTrigger({ value, className, children, ...props }: TabsTriggerProps) {
const { value: selected, onValueChange } = useContext(TabsCtx);
const isActive = selected === value;
const opacity = useSharedValue(isActive ? 1 : 0);
React.useEffect(() => { opacity.value = withTiming(isActive ? 1 : 0, { duration: 150 }); }, [isActive, opacity]);
const bgStyle = useAnimatedStyle(() => ({ position: "absolute" as const, inset: 0, borderRadius: 6, backgroundColor: "hsl(0, 0%, 100%)", opacity: opacity.value }));
return (
<Pressable className={cn("flex-1 items-center justify-center py-2 min-h-12 relative", className)} onPress={() => onValueChange(value)} accessible={true} accessibilityRole="tab" accessibilityState={{ selected: isActive }} {...props}>
<Animated.View style={bgStyle} />
{typeof children === "string" ? <Text className={cn("text-sm font-medium", isActive ? "text-foreground" : "text-muted-foreground")}>{children}</Text> : children}
</Pressable>
);
}
export interface TabsContentProps extends React.ComponentPropsWithoutRef<typeof View> {
className?: string;
value: string;
children?: React.ReactNode;
}
export function TabsContent({ value, className, ...props }: TabsContentProps) {
const { value: selected } = useContext(TabsCtx);
if (selected !== value) return null;
return <View className={cn("mt-2", className)} {...props} />;
}