iOS Mail Tabs
brandonjt/ios-mail-tabsFreeBlock
A set of animated, iOS-inspired mail tabs for React apps. Switch between tabs like Primary, Transactions, Updates, Promotions, and All Mail with smooth transitions. Great for email clients or any UI needing visually distinct, interactive tab navigation.
Live preview
live · rendered by the registry
Rendered by brandonjt on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://brandonjuliothenaro.my.id/r/ios-mail-tabs.jsonSource
1import { cn } from "@/lib/utils";2import { LucideIcon } from "lucide-react";3import { AnimatePresence, motion } from "motion/react";4import * as React from "react";56// Context for managing selected tab7interface IosMailTabsContextProps {8 value: string;9 setValue: (value: string) => void;10}11const IosMailTabsContext = React.createContext<12 IosMailTabsContextProps | undefined13>(undefined);1415/**16 * Custom hook to access IosMailTabs context.17 * Throws if used outside of <IosMailTabs>.18 * @example19 * const { value, setValue } = useIosMailTabs();20 */21export function useIosMailTabs(): IosMailTabsContextProps {22 const ctx = React.useContext(IosMailTabsContext);23 if (!ctx) throw new Error("useIosMailTabs must be used within <IosMailTabs>");24 return ctx;25}2627/**28 * Root component for iOS-style tabs.29 * @example30 * <IosMailTabs defaultValue="primary">31 * <IosMailTabsList>32 * <IosMailTabsTrigger value="primary" icon={UserRoundIcon} className="bg-sky-600 text-white">33 * Primary34 * </IosMailTabsTrigger>35 * </IosMailTabsList>36 * <IosMailTabsContent value="primary">Primary content</IosMailTabsContent>37 * </IosMailTabs>38 */39export function IosMailTabs({40 defaultValue,41 value: controlledValue,42 onValueChange,43 children,44 className,45}: {46 defaultValue?: string;47 value?: string;48 onValueChange?: (value: string) => void;49 children: React.ReactNode;50 className?: string;51}) {52 const [uncontrolledValue, setUncontrolledValue] = React.useState(53 defaultValue ?? "",54 );55 const isControlled = controlledValue !== undefined;56 const value = isControlled ? controlledValue : uncontrolledValue;57 const setValue = React.useCallback(58 (v: string) => {59 if (!isControlled) setUncontrolledValue(v);60 onValueChange?.(v);61 },62 [isControlled, onValueChange],63 );6465 return (66 <IosMailTabsContext.Provider value={{ value, setValue }}>67 <div className={cn("flex flex-col gap-2", className)}>{children}</div>68 </IosMailTabsContext.Provider>69 );70}7172/**73 * List container for iOS-style tab triggers.74 * @example75 * <IosMailTabsList>76 * <IosMailTabsTrigger value="primary" icon={UserRoundIcon} className="bg-sky-600 text-white">77 * Primary78 * </IosMailTabsTrigger>79 * </IosMailTabsList>80 */81export function IosMailTabsList({82 children,83 className,84}: {85 children: React.ReactNode;86 className?: string;87}) {88 return (89 <div90 className={cn(91 "flex flex-row items-center justify-center gap-1.5",92 className,93// … truncated
What it pulls in
npm packages
Files it writes
More from brandonjt
All 2 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Image Zoomimage-zoom | brandonjt | Blocks | Free | 2 deps |
