ComponentsScrollable Card Stack

Scrollable Card Stack

A vertical carousel component with 3D perspective effects that displays cards in a stacked layout. Features smooth scrolling animations, touch support, and configurable 3D transformations including scale, rotation, and opacity changes.

Card 1 of 4 selected. Use arrow keys to navigate one card at a time, or click the dots below.

Code

1

Install with shadcn

Terminal

npx shadcn@latest add @smoothui/scrollable-card-stack

Or install the demo

Terminal

npx shadcn@latest add @smoothui/scrollable-card-stack-demo

How to use

Demo.tsx

"use client"

import { getAllPeople, getAvatarUrl } from "@/app/doc/data/peopleData"

import ScrollableCardStack from "../ui/ScrollableCardStack"

export default function ScrollableCardStackDemo() {
  const people = getAllPeople()

  const cardData = [
    {
      id: "siriorb",
      name: people[0]?.name || "Edu Calvo",
      handle: "@educalvolpz",
      avatar:
        "https://res.cloudinary.com/dyzxnud9z/image/upload/w_40,h_40,c_fill,g_auto/v1759818651/smoothui/educalvolpz.jpg", // Keep educlopez as requested
      video:
        "https://res.cloudinary.com/dyzxnud9z/video/upload/smoothui/siriorb.mp4",
      href: "https://x.com/educalvolpz",
    },
    {
      id: "richpopover",
      name: people[1]?.name || "Sarah Chen",
      handle: `@${people[1]?.name?.toLowerCase().replace(/\s+/g, "") || "sarahchen"}`,
      avatar: getAvatarUrl(people[1]?.avatar || "", 40),
      video:
        "https://res.cloudinary.com/dyzxnud9z/video/upload/smoothui/richpopover.mp4",
      href: `https://x.com/${people[1]?.name?.toLowerCase().replace(/\s+/g, "") || "sarahchen"}`,
    },
    {
      id: "sparkbites",
      name: people[2]?.name || "Marcus Johnson",
      handle: `@${people[2]?.name?.toLowerCase().replace(/\s+/g, "") || "marcusj"}`,
      avatar: getAvatarUrl(people[2]?.avatar || "", 40),
      video:
        "https://res.cloudinary.com/dyzxnud9z/video/upload/smoothui/sparkbites.mp4",
      href: `https://x.com/${people[2]?.name?.toLowerCase().replace(/\s+/g, "") || "marcusj"}`,
    },
    {
      id: "svgl",
      name: people[3]?.name || "Emily Rodriguez",
      handle: `@${people[3]?.name?.toLowerCase().replace(/\s+/g, "") || "emilyrodriguez"}`,
      avatar: getAvatarUrl(people[3]?.avatar || "", 40),
      video:
        "https://res.cloudinary.com/dyzxnud9z/video/upload/smoothui/svgl.mp4",
      href: `https://x.com/${people[3]?.name?.toLowerCase().replace(/\s+/g, "") || "emilyrodriguez"}`,
    },
  ]

  return (
    <div className="mx-auto w-full max-w-md">
      <ScrollableCardStack
        items={cardData}
        cardHeight={200}
        perspective={1200}
        transitionDuration={200}
        className="mx-auto"
      />
    </div>
  )
}

Props

PropTypeDefault
items
React.ReactNode[]
-
cardHeight?
number
-
perspective?
number
-
transitionDuration?
number
-
className?
string
-