Docs
Fluid Tabs
The component is a sliding animation card
Installation
Install dependencies
npm install framer-motion lucide-react
Update tailwind.config.js
Add the following to your tailwind.config.js file.
module.exports = {
theme: {
extend: {
}
}
}
Run the following command
It will create a new file fluid-tabs.tsx
inside the components/animata/card
directory.
mkdir -p components/animata/card && touch components/animata/card/fluid-tabs.tsx
Paste the code
Open the newly created file and paste the following code:
"use client";
import { useEffect, useRef, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Inbox, Landmark, PieChart } from "lucide-react";
const tabs = [
{
id: "accounts",
label: "Accounts",
icon: <Landmark size={18} />,
},
{
id: "deposits",
label: "Deposits",
icon: <Inbox size={18} />,
},
{
id: "funds",
label: "Funds",
icon: <PieChart size={18} />,
},
];
export default function FluidTabs() {
const [activeTab, setActiveTab] = useState("funds");
const [touchedTab, setTouchedTab] = useState<string | null>(null);
const [prevActiveTab, setPrevActiveTab] = useState("funds");
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
const handleTabClick = (tabId: string) => {
setPrevActiveTab(activeTab);
setActiveTab(tabId);
setTouchedTab(tabId);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
setTouchedTab(null);
}, 300);
};
const getTabIndex = (tabId: string) => tabs.findIndex((tab) => tab.id === tabId);
return (
<div className="flex items-center justify-center py-4">
<div className="relative flex w-full max-w-md space-x-2 overflow-hidden rounded-full bg-[#f5f1eb] p-1 shadow-lg">
<AnimatePresence initial={false}>
<motion.div
key={activeTab}
className="absolute inset-y-0 my-1 rounded-full bg-white"
initial={{ x: `${getTabIndex(prevActiveTab) * 100}%` }}
animate={{ x: `${getTabIndex(activeTab) * 100}%` }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
style={{ width: `${100 / tabs.length}%` }}
/>
</AnimatePresence>
{tabs.map((tab) => (
<motion.button
key={tab.id}
className={`relative z-10 flex w-full items-center justify-center gap-1.5 px-5 py-3 text-sm font-bold transition-colors duration-300 ${
activeTab === tab.id ? "font-bold text-black" : "text-gray-500"
} ${touchedTab === tab.id ? "blur-sm" : ""}`}
onClick={() => handleTabClick(tab.id)}
>
{tab.icon}
{tab.label}
</motion.button>
))}
</div>
</div>
);
}
Credits
Built by Rudra Sankha Sinhamahapatra
Twitter Handle Rudra Sankha