Swiping Cards

Swipe Through Cards with Images Just Like in the Tinder App

Installation


Install dependencies

npm i classnames framer-motion

Copy the CSS Markup

Copy The JSX Source Code

/components/swipe-cards/swipecards.jsx
"use client";
import React, { useState } from "react";
import {
  motion,
  useMotionValue,
  useTransform,
  AnimatePresence,
} from "framer-motion";
import classNames from "classnames";

const SWIPE_THRESHOLD = 330;

const SwipeCards = ({ Images }) => {
  const [currentIndex, setCurrentIndex] = useState(0);

  const dragX = useMotionValue(0);

  const onDragEnd = (_, info) => {
    const offset = info.offset.x;
    if (Math.abs(offset) > SWIPE_THRESHOLD) {
      setCurrentIndex((prevIndex) => (prevIndex + 1) % Images.length);
      dragX.set(0);
    }
  };

  const rotate = useTransform(dragX, [-200, 200], [-15, 15], { clamp: true });

  return (
    <div className="h-[85vh] relative flex justify-center items-center">
      <div className="h-[75vh] w-[22vw] relative">
        <AnimatePresence initial={true}>
          {Images.map((image, index) => (
            <motion.div
              key={index}
              style={{
                backgroundImage: `url(${image})`,
                backgroundSize: "cover",
                backgroundPosition: "center",
                width: "100%",
                height: "100%",
                position: "absolute",
                top: 0,
                left: 0,
                rotate: index === currentIndex ? rotate : 0,
                boxShadow: 10,
              }}
              drag={index === currentIndex ? "x" : false}
              dragConstraints={{ left: 0, right: 0 }}
              dragElastic={1}
              onDragEnd={onDragEnd}
              initial={
                index === currentIndex ? { scale: 0.95, opacity: 0 } : null
              }
              animate={
                index === currentIndex
                  ? { scale: 1, opacity: 1, y: 0 }
                  : { scale: 0.95, opacity: 0.8 }
              }
              transition={{ type: "spring", stiffness: 300, damping: 20 }}
              className={classNames(
                "cursor-grab active:cursor-grabbing rounded-2xl",
                index === currentIndex ? "z-10" : "z-0",
              )}
            />
          ))}
        </AnimatePresence>
      </div>
    </div>
  );
};

export default SwipeCards;