Replies: 4 comments 11 replies
-
@pexllecn did you find any solution to achieve this? |
Beta Was this translation helpful? Give feedback.
-
I was also looking for a solution, and came across this https://shadcn-solid.com/docs/components/tabs |
Beta Was this translation helpful? Give feedback.
-
Hey everyone! I made a project called Shadcn/UI - Animated Tabs that adds smooth animations to the Shadcn tabs component in Next.js. 🔗 Relevant Code Changes: tabs.tsx |
Beta Was this translation helpful? Give feedback.
-
I improved the @list-jonas code for better performance: (also you can add debounce if you want) "use client";
import { cn } from "@/lib/utils";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import * as React from "react";
import { useEffect, useRef, useState } from "react";
const Tabs = TabsPrimitive.Root;
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => {
const [indicatorStyle, setIndicatorStyle] = useState({
left: 0,
top: 0,
width: 0,
height: 0,
});
const tabsListRef = useRef<HTMLDivElement | null>(null);
const updateIndicator = React.useCallback(() => {
if (!tabsListRef.current) return;
const activeTab = tabsListRef.current.querySelector<HTMLElement>('[data-state="active"]');
if (!activeTab) return;
const activeRect = activeTab.getBoundingClientRect();
const tabsRect = tabsListRef.current.getBoundingClientRect();
// Use requestAnimationFrame for smoother animations
requestAnimationFrame(() => {
setIndicatorStyle({
left: activeRect.left - tabsRect.left,
top: activeRect.top - tabsRect.top,
width: activeRect.width,
height: activeRect.height,
});
});
}, []);
useEffect(() => {
// Initial update
const timeoutId = setTimeout(updateIndicator, 0);
// Event listeners
window.addEventListener("resize", updateIndicator);
const observer = new MutationObserver(updateIndicator);
if (tabsListRef.current) {
observer.observe(tabsListRef.current, {
attributes: true,
childList: true,
subtree: true,
});
}
return () => {
clearTimeout(timeoutId);
window.removeEventListener("resize", updateIndicator);
observer.disconnect();
};
}, [updateIndicator]);
return (
<div className="relative" ref={tabsListRef}>
<TabsPrimitive.List
ref={ref}
className={cn(
"relative inline-flex items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
<div
className="absolute rounded-md bg-background shadow-sm transition-all duration-300 ease-in-out pointer-events-none"
style={indicatorStyle}
/>
</div>
);
});
TabsList.displayName = TabsPrimitive.List.displayName;
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground z-10",
className
)}
{...props}
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsContent, TabsList, TabsTrigger }; |
Beta Was this translation helpful? Give feedback.
-
can someone help in achieving this animation here for the tabs component https://play.tailwindcss.com/4DRvkUZTvK
Beta Was this translation helpful? Give feedback.
All reactions