Flip Card
A card component that animates flipping between front and back, ideal for info cards, games, or interactive content.
Install the following dependencies:
pnpm install motion
Copy and paste the following code into your project:
1import React, { useState, useImperativeHandle, forwardRef } from "react";2import { Easing, motion } from "motion/react";3import { cn } from "@/lib/utils";45interface FlipCardProps {6 frontContent: React.ReactNode;7 backContent: React.ReactNode;8 width?: string;9 height?: string;10 flipDirection?: "horizontal" | "vertical";11 className?: string;12 disabled?: boolean;13 initialFlipped?: boolean;14 onFlip?: (isFlipped: boolean) => void;15 flipTrigger?: "click" | "hover" | "manual";16 duration?: number;17 easing?: Easing | Easing[];18}1920export interface FlipCardHandle {21 flip: () => void;22 setFlipped: (flipped: boolean) => void;23 isFlipped: boolean;24}2526const FlipCard = forwardRef<FlipCardHandle, FlipCardProps>(27 (28 {29 frontContent,30 backContent,31 width = "w-80",32 height = "h-96",33 flipDirection = "horizontal",34 className,35 disabled = false,36 initialFlipped = false,37 onFlip,38 flipTrigger = "click",39 duration = 0.6,40 easing = "easeInOut",41 },42 ref43 ) => {44 const [isFlipped, setIsFlipped] = useState(initialFlipped);4546 const handleFlip = () => {47 if (disabled || flipTrigger === "manual") return;48 const newFlippedState = !isFlipped;49 setIsFlipped(newFlippedState);50 onFlip?.(newFlippedState);51 };5253 const handleMouseEnter = () => {54 if (flipTrigger === "hover" && !disabled) {55 setIsFlipped(true);56 onFlip?.(true);57 }58 };5960 const handleMouseLeave = () => {61 if (flipTrigger === "hover" && !disabled) {62 setIsFlipped(false);63 onFlip?.(false);64 }65 };6667 useImperativeHandle(68 ref,69 () => ({70 flip: () => {71 const newFlippedState = !isFlipped;72 setIsFlipped(newFlippedState);73 onFlip?.(newFlippedState);74 },75 setFlipped: (flipped: boolean) => {76 setIsFlipped(flipped);77 onFlip?.(flipped);78 },79 get isFlipped() {80 return isFlipped;81 },82 }),83 [isFlipped, onFlip]84 );8586 const flipVariants = {87 horizontal: {88 front: {89 rotateY: isFlipped ? 180 : 0,90 },91 back: {92 rotateY: isFlipped ? 0 : -180,93 },94 },95 vertical: {96 front: {97 rotateX: isFlipped ? 180 : 0,98 },99 back: {100 rotateX: isFlipped ? 0 : -180,101 },102 },103 };104105 return (106 <div107 className={cn(108 width,109 height,110 "perspective-1000",111 !disabled && flipTrigger === "click" && "cursor-pointer",112 className113 )}114 onClick={flipTrigger === "click" ? handleFlip : undefined}115 onMouseEnter={handleMouseEnter}116 onMouseLeave={handleMouseLeave}117 >118 <motion.div119 className="relative w-full h-full"120 style={{ transformStyle: "preserve-3d" }}121 >122 <motion.div123 className="absolute inset-0"124 variants={flipVariants[flipDirection]}125 animate="front"126 transition={{ duration, ease: easing }}127 style={{ backfaceVisibility: "hidden" }}128 >129 {frontContent}130 </motion.div>131132 <motion.div133 className="absolute inset-0"134 variants={flipVariants[flipDirection]}135 animate="back"136 transition={{ duration, ease: easing }}137 style={{138 backfaceVisibility: "hidden",139 transform:140 flipDirection === "horizontal"141 ? "rotateY(-180deg)"142 : "rotateX(-180deg)",143 }}144 >145 {backContent}146 </motion.div>147 </motion.div>148 </div>149 );150 }151);152153export { FlipCard };
Usage
1import { FlipCard } from "@/components/ui/flip-card";2import React, { useRef } from "react";34export function Example() {5 const flipCardRef = useRef(null);67 return (8 <>9 <FlipCard10 ref={flipCardRef}11 width="w-64"12 height="h-80"13 frontContent={14 <div className="flex flex-col items-center justify-center h-full w-full bg-primary text-primary-foreground rounded-lg p-4">15 <span className="text-lg font-semibold">Front Side</span>16 <span className="text-sm mt-2">This is the front side of the card.</span>17 </div>18 }19 backContent={20 <div className="flex flex-col items-center justify-center h-full w-full bg-secondary text-secondary-foreground rounded-lg p-4">21 <span className="text-lg font-semibold">Back Side</span>22 <span className="text-sm mt-2">This is the back side of the card.</span>23 </div>24 }25 flipDirection="horizontal"26 flipTrigger="click"27 />28 </>29 );30}
Props
Prop | Type | Description | Default |
---|---|---|---|
frontContent | ReactNode | Content to be displayed on the front of the card. | - |
backContent | ReactNode | Content to be displayed on the back of the card. | - |
width | string | Card width (Tailwind class). | "w-80" |
height | string | Card height (Tailwind class). | "h-96" |
flipDirection | "horizontal" | "vertical" | Flip direction (horizontal/vertical). | "horizontal" |
className | string | Extra Tailwind/customization classes. | - |
disabled | boolean | Disables the card. | false |
initialFlipped | boolean | Determines if the card is initially flipped. | false |
onFlip | (isFlipped: boolean) => void | Called when the card is flipped. | - |
flipTrigger | "click" | "hover" | "manual" | Flip trigger (click, hover, manual). | "click" |
duration | number | Animation duration (seconds). | 0.6 |
easing | Easing | Easing[] | Animation curve. | "easeInOut" |