We are working on new components <3
HextaUIHextaUI
ApplicationAnimated Tabs

Animated Tabs

Animated Tabs

Preview

Code

AnimatedTabs.tsx
"use client";
 
import { useEffect, useRef, useState } from "react";
 
export function AnimatedTabs({ tabs }: { tabs: { label: string }[] }) {
  const [activeTab, setActiveTab] = useState(tabs[0].label);
  const containerRef = useRef<HTMLDivElement>(null);
  const activeTabRef = useRef<HTMLButtonElement>(null);
 
  useEffect(() => {
    const container = containerRef.current;
 
    if (container && activeTab) {
      const activeTabElement = activeTabRef.current;
 
      if (activeTabElement) {
        const { offsetLeft, offsetWidth } = activeTabElement;
 
        const clipLeft = offsetLeft + 16;
        const clipRight = offsetLeft + offsetWidth + 16;
 
        container.style.clipPath = `inset(0 ${Number(
          100 - (clipRight / container.offsetWidth) * 100,
        ).toFixed()}% 0 ${Number(
          (clipLeft / container.offsetWidth) * 100,
        ).toFixed()}% round 17px)`;
      }
    }
  }, [activeTab, activeTabRef, containerRef]);
 
  return (
    <div className="relative mx-auto flex w-fit flex-col items-center rounded-full bg-secondary/50 py-2 px-4">
      <div
        ref={containerRef}
        className="absolute z-10 w-full overflow-hidden [clip-path:inset(0px_75%_0px_0%_round_17px)] [transition:clip-path_0.25s_ease]"
      >
        <div className="relative flex w-full justify-center bg-primary">
          {tabs.map((tab, index) => (
            <button
              key={index}
              onClick={() => setActiveTab(tab.label)}
              className="flex h-8 items-center rounded-full p-3 text-sm font-medium text-primary-foreground"
              tabIndex={-1}
            >
              {tab.label}
            </button>
          ))}
        </div>
      </div>
      <div className="relative flex w-full justify-center">
        {tabs.map(({ label }, index) => {
          const isActive = activeTab === label;
 
          return (
            <button
              key={index}
              ref={isActive ? activeTabRef : null}
              onClick={() => setActiveTab(label)}
              className="flex h-8 items-center rounded-full p-3 text-sm font-medium text-muted-foreground"
            >
              {label}
            </button>
          );
        })}
      </div>
    </div>
  );
}

Usage

index.tsx
import { AnimatedTabs } from "@/components/library/application/AnimatedTabs.tsx";
 
<AnimatedTabs
  tabs={[
    { label: "Home" },
    { label: "About" },
    { label: "Resources" },
    { label: "Docs" },
    { label: "Support" },
  ]}
/>;

Props

PropTypeDefault
tabs
{ label: string; }[]
-
Edit on GitHub

Last updated on

On this page