Skip to content
Docs
Flip Card

Flip 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.tsx

Paste 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 / description props with composable FlipCard.Front and FlipCard.Back faces.

Credits

Built by Bibek Bhattarai