NRVM

Back to Discover
CardsIntermediate

Swipeable Stack

A stack of cards that you can swipe away, built with Framer Motion drag.

Live Preview
1

Next card

2

Next card

3

Swipe left or right

"use client";
import { useState } from "react";
import { motion } from "framer-motion";

export function SwipeableStack() {
  const [cards, setCards] = useState([1, 2, 3]);

  return (
    <div className="flex flex-col items-center justify-center w-full h-[400px] bg-[#0a0a0a] rounded-xl border border-border overflow-hidden p-6">
      <div className="relative w-48 h-64 mt-4">
        {cards.map((card, index) => {
          const isTop = index === cards.length - 1;
          return (
            <motion.div
              key={card}
              drag={isTop ? "x" : false}
              dragConstraints={{ left: 0, right: 0 }}
              dragElastic={1}
              onDragEnd={(e, { offset, velocity }) => {
                const swipe = Math.abs(offset.x) * velocity.x;
                if (swipe < -10000 || swipe > 10000) {
                  setCards((prev) => prev.filter((c) => c !== card));
                }
              }}
              className="absolute inset-0 flex flex-col items-center justify-center bg-[#151515] rounded-2xl border border-border shadow-xl origin-bottom cursor-grab active:cursor-grabbing"
              style={{
                zIndex: index,
              }}
              animate={{
                scale: 1 - (cards.length - 1 - index) * 0.05,
                y: (cards.length - 1 - index) * -10,
              }}
              transition={{ type: "spring", stiffness: 300, damping: 20 }}
            >
              <span className="text-4xl font-bold text-muted-foreground select-none">{card}</span>
              <p className="text-xs text-muted-foreground mt-4 text-center px-4 select-none">
                {isTop ? "Swipe left or right" : "Next card"}
              </p>
            </motion.div>
          );
        })}
        {cards.length === 0 && (
          <div className="absolute inset-0 flex flex-col items-center justify-center">
            <span className="text-sm text-muted-foreground mb-4">All done!</span>
            <button 
              onClick={() => setCards([1, 2, 3])}
              className="px-4 py-2 text-xs font-medium rounded-md bg-foreground text-background"
            >
              Reset Stack
            </button>
          </div>
        )}
      </div>
    </div>
  );
}