Card Stack
ui-beats/card-stackFreeComponent
The CardStack component deals your content as a deck: drag the top card aside to throw it away, the cards behind rise a step, and the dismissed card loops around to the back.
Install it
npx shadcn@latest add https://uibeats.com/r/card-stack.jsonSource
1"use client";23import {4 AnimatePresence,5 motion,6 useReducedMotion,7 type PanInfo,8} from "motion/react";9import {10 Children,11 useCallback,12 useEffect,13 useRef,14 useState,15 type KeyboardEvent,16 type ReactNode,17} from "react";18import { cn } from "@/lib/utils";1920interface CardStackProps {21 /** One card per child. Each is given the stack's own card chrome. */22 children: ReactNode;23 /** How many cards are rendered, counting the one on top. */24 visible?: number;25 /** Vertical gap between each card and the one in front of it, in pixels. */26 offset?: number;27 /** How much smaller each card is than the one in front of it. */28 scaleStep?: number;29 /** Milliseconds between automatic advances. `0` turns autoplay off. */30 autoplay?: number;31 /** Distance, in pixels, a card must be dragged before it is dismissed. */32 dismissAt?: number;33 className?: string;34}3536/**37 * A deck of cards you can throw away: drag the top one aside and it flies off,38 * the rest rise a step, and the dismissed card returns to the back of an39 * endless loop.40 *41 * Only `visible` cards are ever mounted, so a hundred-card deck costs the same42 * as a three-card one. Keys carry the lap number as well as the card index,43 * which is what lets a card leave and come back: without it a short deck would44 * find the dismissed card already in the window and shrink it into the stack45 * instead of dealing it away.46 */47export function CardStack({48 children,49 visible = 3,50 offset = 14,51 scaleStep = 0.05,52 autoplay = 0,53 dismissAt = 90,54 className = "",55}: CardStackProps) {56 const cards = Children.toArray(children);57 const prefersReducedMotion = useReducedMotion();5859 const [index, setIndex] = useState(0);60 const [direction, setDirection] = useState(1);61 const [paused, setPaused] = useState(false);62 const dragging = useRef(false);6364 const advance = useCallback(65 (towards: 1 | -1 = 1) => {66 if (cards.length === 0) return;67 setDirection(towards);68 setIndex((current) => (current + 1) % cards.length);69 },70 [cards.length],71 );7273 /*74 * Autoplay pauses on hover, focus and drag.75 *76 * A deck that keeps dealing while someone is reading the card in front of77 * them — or halfway through dragging it — is actively working against them.78 */79 useEffect(() => {80 if (!autoplay || paused || cards.length < 2) return;81 const timer = setTimeout(() => advance(1), autoplay);82 return () => clearTimeout(timer);83// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from ui-beats
All 34 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Beamanimated-beam | ui-beats | Components | Free | 1 dep | |
| Animated Listanimated-list | ui-beats | Components | Free | 1 dep | |
| Border Beamborder-beam | ui-beats | Components | Free | 1 dep | |
| Bouncebounce | ui-beats | Components | Free | 1 dep | |
| Dockdock | ui-beats | Components | Free | 1 dep | |
| Fade Infade-in | ui-beats | Components | Free | 1 dep | |
| Fade In Unblurfade-in-unblur | ui-beats | Components | Free | 1 dep | |
| Flip Cardflip-card | ui-beats | Components | Free | 2 deps |
