Animated Tabs
smoothui-registry/animated-tabsFreeComponent
Animated tabs component with sliding indicator
Install it
npx shadcn@latest add https://www.smoothui.dev/r/animated-tabs.jsonSource
1"use client";23import { cn } from "@/lib/utils";4import { motion, useReducedMotion } from "motion/react";5import { type ReactNode, useCallback, useId, useState } from "react";67export interface AnimatedTabsProps {8 activeTab?: string;9 className?: string;10 defaultTab?: string;11 layoutId?: string;12 onChange?: (tabId: string) => void;13 tabs: { id: string; label: string; icon?: ReactNode }[];14 variant?: "underline" | "pill" | "segment";15}1617const SPRING = {18 bounce: 0.05,19 duration: 0.25,20 type: "spring" as const,21};2223export default function AnimatedTabs({24 tabs,25 activeTab: controlledActiveTab,26 defaultTab,27 onChange,28 variant = "underline",29 layoutId: customLayoutId,30 className,31}: AnimatedTabsProps) {32 const shouldReduceMotion = useReducedMotion();33 const generatedId = useId();34 const layoutId = customLayoutId ?? `animated-tabs-${generatedId}`;3536 const [internalActiveTab, setInternalActiveTab] = useState(37 defaultTab ?? tabs[0]?.id ?? ""38 );3940 const isControlled = controlledActiveTab !== undefined;41 const activeTab = isControlled ? controlledActiveTab : internalActiveTab;4243 const handleTabChange = useCallback(44 (tabId: string) => {45 if (!isControlled) {46 setInternalActiveTab(tabId);47 }48 onChange?.(tabId);49 },50 [isControlled, onChange]51 );5253 const handleKeyDown = useCallback(54 (event: React.KeyboardEvent, currentIndex: number) => {55 let newIndex = currentIndex;5657 if (event.key === "ArrowRight") {58 event.preventDefault();59 newIndex = (currentIndex + 1) % tabs.length;60 } else if (event.key === "ArrowLeft") {61 event.preventDefault();62 newIndex = (currentIndex - 1 + tabs.length) % tabs.length;63 } else if (event.key === "Home") {64 event.preventDefault();65 newIndex = 0;66 } else if (event.key === "End") {67 event.preventDefault();68 newIndex = tabs.length - 1;69 } else {70 return;71 }7273 const newTab = tabs[newIndex];74 if (newTab) {75 handleTabChange(newTab.id);76 const tabElement = document.getElementById(77 `${layoutId}-tab-${newTab.id}`78 );79 tabElement?.focus();80 }81 },82 [tabs, handleTabChange, layoutId]83 );8485 const baseContainerStyles = cn(86 "relative inline-flex",87 variant === "underline" && "gap-1 border-border border-b",88 variant === "pill" && "gap-1 rounded-full bg-muted p-1",89 variant === "segment" && "gap-0 rounded-lg bg-muted p-1"90// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from SmoothUI Registry
All 178 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Agent Avataragent-avatar | SmoothUI Registry | Components | Free | no deps | |
| Ai Approvalai-approval | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Artifactai-artifact | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Branchai-branch | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Citationai-citation | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Context Meterai-context-meter | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Conversationai-conversation | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Coreai-core | SmoothUI Registry | Components | Free | 1 dep |
