Docs
Flip CardFlip Card
A dynamic flip card featuring smooth 180-degree flip animations along both the X and Y axes.
requires interactionhover
Loading...
Installation
CLI
pnpm dlx shadcn@latest add https://animata.design/r/card/flip-card.json
Manual
Run the following command
It will create a new file called flip-card.tsx inside the components/animata/card directory.
mkdir -p components/animata/card && touch components/animata/card/flip-card.tsxPaste the code
Open the newly created file and paste the following code:
"use client";
import { type ComponentProps, createContext, use, useMemo } from "react";
import { cn } from "@/lib/utils";
export type FlipCardRotate = "x" | "y";
type FlipCardContextValue = {
rotate: FlipCardRotate;
};
const FlipCardContext = createContext<FlipCardContextValue | null>(null);
const ROTATION_CLASS = {
x: {
hover: "group-hover/card:rotate-x-180",
back: "rotate-x-180",
},
y: {
hover: "group-hover/card:rotate-y-180",
back: "rotate-y-180",
},
} as const;
function useFlipCard() {
const context = use(FlipCardContext);
if (!context) {
throw new Error("FlipCard.Front and FlipCard.Back must be used within <FlipCard>.");
}
return context;
}
type FlipCardRootProps = ComponentProps<"div"> & {
rotate?: FlipCardRotate;
};
function FlipCardRoot({ rotate = "y", className, children, ...props }: FlipCardRootProps) {
const value = useMemo(() => ({ rotate }), [rotate]);
return (
<FlipCardContext.Provider value={value}>
<div className={cn("group/card h-72 w-56 perspective-[1000px]", className)} {...props}>
<div
className={cn(
"relative h-full rounded-2xl transition-transform duration-500 ease-out transform-3d will-change-transform motion-reduce:transition-none",
ROTATION_CLASS[rotate].hover,
)}
>
{children}
</div>
</div>
</FlipCardContext.Provider>
);
}
type FlipCardFaceProps = ComponentProps<"div">;
function FlipCardFront({ className, ...props }: FlipCardFaceProps) {
useFlipCard();
return <div className={cn("absolute inset-0 backface-hidden", className)} {...props} />;
}
function FlipCardBack({ className, ...props }: FlipCardFaceProps) {
const { rotate } = useFlipCard();
return (
<div
className={cn("absolute inset-0 backface-hidden", ROTATION_CLASS[rotate].back, className)}
{...props}
/>
);
}
const FlipCard = Object.assign(FlipCardRoot, {
Front: FlipCardFront,
Back: FlipCardBack,
}) as typeof FlipCardRoot & {
Front: typeof FlipCardFront;
Back: typeof FlipCardBack;
};
export default FlipCard;
export { FlipCard, FlipCardBack, FlipCardFront, FlipCardRoot };Usage
FlipCard is bare bones: perspective, hover flip, and front/back faces. You bring the markup and styling.
Pass rotate="x" or rotate="y" (default) on the root. Style each face with className on FlipCard.Front and FlipCard.Back.
import FlipCard from "@/animata/card/flip-card";
<FlipCard rotate="y" className="h-72 w-56">
<FlipCard.Front>
<img src="/photo.jpg" alt="Programming" className="h-full w-full rounded-2xl object-cover" />
<div className="absolute bottom-4 left-4 text-xl font-bold text-white">Programming</div>
</FlipCard.Front>
<FlipCard.Back className="rounded-2xl bg-black/80 p-4 text-slate-200">
<h2 className="text-base font-bold text-white">What is programming?</h2>
<p className="mt-4 text-base leading-normal text-gray-100">Your copy here.</p>
</FlipCard.Back>
</FlipCard>Changelog
- 2026-06 — Replaced fixed
image/title/descriptionprops with composableFlipCard.FrontandFlipCard.Backfaces.
Credits
Built by Bibek Bhattarai