All components
Card Stack
cardsAceternity UI component.
responsive · 480px
Install
Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:
$
npx shadcn@latest add https://your-domain/r/card-stack.jsonUsage
"use client";
import { CardStack } from "@/registry/aceternity-ui/card-stack";
const CARDS = [
{
id: 0,
name: "Alice Johnson",
designation: "Senior Engineer at Vercel",
content: (
<p>
These cards are absolutely gorgeous. The stacking animation is so smooth
and the design is clean. I use them in all my projects now.
</p>
),
},
{
id: 1,
name: "Bob Martinez",
designation: "Product Designer at Figma",
content: (
<p>
The attention to detail is incredible. Every pixel is perfectly placed
and the interactions feel natural and delightful.
</p>
),
},
{
id: 2,
name: "Carol Chen",
designation: "CTO at Startup",
content: (
<p>
We integrated these components into our design system and the team loves
them. They save us so much time and always look professional.
</p>
),
},
];
export default function Demo() {
return (
<div className="flex items-center justify-center w-full py-20">
<CardStack items={CARDS} />
</div>
);
}Component source
"use client";
import { useEffect, useState } from "react";
import { motion } from "motion/react";
let interval: any;
type Card = {
id: number;
name: string;
designation: string;
content: React.ReactNode;
};
export const CardStack = ({
items,
offset,
scaleFactor,
}: {
items: Card[];
offset?: number;
scaleFactor?: number;
}) => {
const CARD_OFFSET = offset || 10;
const SCALE_FACTOR = scaleFactor || 0.06;
const [cards, setCards] = useState<Card[]>(items);
useEffect(() => {
startFlipping();
return () => clearInterval(interval);
}, []);
const startFlipping = () => {
interval = setInterval(() => {
setCards((prevCards: Card[]) => {
const newArray = [...prevCards]; // create a copy of the array
newArray.unshift(newArray.pop()!); // move the last element to the front
return newArray;
});
}, 5000);
};
return (
<div className="relative h-60 w-60 md:h-60 md:w-96">
{cards.map((card, index) => {
return (
<motion.div
key={card.id}
className="absolute dark:bg-black bg-white h-60 w-60 md:h-60 md:w-96 rounded-3xl p-4 shadow-xl border border-neutral-200 dark:border-white/[0.1] shadow-black/[0.1] dark:shadow-white/[0.05] flex flex-col justify-between"
style={{
transformOrigin: "top center",
}}
animate={{
top: index * -CARD_OFFSET,
scale: 1 - index * SCALE_FACTOR, // decrease scale for cards that are behind
zIndex: cards.length - index, // decrease z-index for the cards that are behind
}}
>
<div className="font-normal text-neutral-700 dark:text-neutral-200">
{card.content}
</div>
<div>
<p className="text-neutral-500 font-medium dark:text-white">
{card.name}
</p>
<p className="text-neutral-400 font-normal dark:text-neutral-200">
{card.designation}
</p>
</div>
</motion.div>
);
})}
</div>
);
};Dependencies
motion
Source: Aceternity UI