shadimage
Forgeui
uianimated-tabssource: ok · hit

Animated Tabs

Switch tabs smoothly with motion-based underline or highlight effects.

npx shadcn add "https://forgeui.in/r/animated-tabs.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Files1 · source cached 10/8/2026, 10:25:04 PM

registry/components/animatedtabs/animatedtabs.tsx
"use client";

import { useState } from "react";
import { motion } from "motion/react";
import { cn } from "@/lib/utils";

type AnimatedTabsProps = {
  tabs?: Array<string>;
  variant?: "default" | "underline";
};

const defaultTabs = ["Home", "Components", "Docs", "Templates"];

const AnimatedTabs = ({
  tabs = defaultTabs,
  variant = "default",
}: AnimatedTabsProps) => {
  const [activeTab, setActiveTab] = useState(tabs[0]);

  if (variant === "underline") {
    return (
      <div className="border-border relative flex items-center border-b">
        {tabs.map((tab, index) => {
          const isActive = activeTab === tab;

          return (
            <button
              key={index}
              type="button"
              onClick={() => setActiveTab(tab)}
              className={cn(
                "relative flex h-10 items-center px-4 text-sm font-medium transition-colors duration-200",
                isActive
                  ? "text-primary"
                  : "text-muted-foreground hover:text-foreground",
              )}
            >
              {isActive && (
                <motion.div
                  layoutId="active-tab-underline"
                  className="bg-primary absolute right-0 bottom-0 left-0 h-0.5"
                  initial={false}
                  transition={{
                    type: "spring",
                    stiffness: 500,
                    damping: 30,
                  }}
                />
              )}
              <span className="relative z-10">{tab}</span>
            </button>
          );
        })}
      </div>
    );
  }

  return (
    <div className="relative mx-auto flex w-fit items-center rounded-full border border-neutral-200/60 bg-neutral-50 p-1 dark:border-neutral-800/70 dark:bg-neutral-900/70">
      {tabs.map((tab, index) => {
        const isActive = activeTab === tab;

        return (
          <button
            key={index}
            type="button"
            onClick={() => setActiveTab(tab)}
            className={cn(
              "relative flex h-8 items-center rounded-full px-3 text-sm font-medium transition-colors duration-200",
              isActive
                ? "text-primary-foreground"
                : "text-muted-foreground hover:text-foreground",
            )}
          >
            {isActive && (
              <motion.div
                layoutId="active-tab-background"
                className="bg-primary absolute inset-0 rounded-full"
                initial={false}
                transition={{
                  type: "spring",
                  stiffness: 500,
                  damping: 30,
                }}
              />
            )}
            <span className="relative z-10">{tab}</span>
          </button>
        );
      })}
    </div>
  );
};

export default AnimatedTabs;

More from Forgeui

Browse all components in this library →