3D Flip Card
A card that flips on a real Y-axis rotation to reveal a back face.
- Category
- Components
- Added
- 2026-08-22
- Deps
- 1
- Updated
- 2026-09-01
Props
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
export function FlipCard({
frontLabel = "Hover to flip",
backLabel = "Here's the back",
trigger = "hover",
}: {
frontLabel?: string;
backLabel?: string;
trigger?: "hover" | "click";
}) {
const [clicked, setClicked] = useState(false);
const flipped = trigger === "hover" ? undefined : clicked;
return (
<div
className="[perspective:1000px]"
onClick={() => trigger === "click" && setClicked((v) => !v)}
>
<motion.div
tabIndex={0}
role={trigger === "click" ? "button" : undefined}
aria-pressed={trigger === "click" ? flipped : undefined}
onKeyDown={(e) => {
if (trigger === "click" && (e.key === "Enter" || e.key === " ")) {
e.preventDefault();
setClicked((v) => !v);
}
}}
className="relative h-40 w-64 cursor-pointer outline-none [transform-style:preserve-3d] focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2"
animate={trigger === "click" ? { rotateY: flipped ? 180 : 0 } : undefined}
whileHover={trigger === "hover" ? { rotateY: 180 } : undefined}
whileFocus={trigger === "hover" ? { rotateY: 180 } : undefined}
transition={{ duration: 0.6, ease: [0.65, 0, 0.35, 1] }}
>
<div className="absolute inset-0 flex items-center justify-center rounded-2xl border border-neutral-200 bg-white p-6 [backface-visibility:hidden]">
<span className="text-center text-lg font-bold">{frontLabel}</span>
</div>
<div
className="absolute inset-0 flex items-center justify-center rounded-2xl bg-red-500 p-6 [backface-visibility:hidden]"
style={{ transform: "rotateY(180deg)" }}
>
<span className="text-center text-lg font-bold text-white">{backLabel}</span>
</div>
</motion.div>
</div>
);
}
About this component
A flip card exists to show two states of the same object — front and back of an ID, a stat then its source — without a cross-fade that makes the two feel unrelated. This one earns the word "3D": the wrapper carries perspective, the rotating element gets transform-style: preserve-3d, and each face has backface-visibility: hidden, so mid-rotation you get real foreshortening instead of two flat layers dissolving into each other. Skip it for anything that needs to stay legible mid-animation — a rotateY pass through 90 degrees is edge-on and blank, so don't put a live form or a running timer on either face. The trigger prop switches which Framer Motion prop drives the rotation, whileHover for hover mode or a controlled animate for click mode, kept mutually exclusive rather than combined, because an active whileHover fighting a controlled animate on the same rotateY value produces visible jitter. The real limitation is the trigger choice itself: hover has nothing to fire on a touchscreen, so a card shipped hover-only stays permanently front-face on mobile unless you branch to click mode.
Curator’s note
backface-visibility:hidden on both faces is the part people skip and then wonder why the back face shows through mirrored mid-flip — it's what makes this a real flip instead of a see-through spin.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Glare Tilt Card
componentsA card that tilts toward the pointer with a true angular specular sweep.
Flip Reveal Tiles
animationsA tile grid that flips on a diagonal wavefront to resolve into one image.