Components / Hover Flip Card
Hover Flip Card
A hover-activated flip card that reveals a background image on the back, with the same content rendered on both sides for a seamless effect.
Preview
Hover over the card to flip between the content side and the image side.
Kendrick Lamar
Kendrick Lamar is a rapper from Compton, California. He is known for his unique style and flow.
Kendrick Lamar
Kendrick Lamar is a rapper from Compton, California. He is known for his unique style and flow.
Usage
tsx
import HoverFlipCard from "@/components/hover-flip-card/hover-flip-card"
export function Example() {
return (
<HoverFlipCard
imageBackground="/images/photo-back.jpg"
className="flex flex-col items-center justify-center p-6 rounded-xl bg-card/80 text-card-foreground shadow-lg backdrop-blur-sm"
>
<h2 className="text-lg font-semibold mb-2">Title</h2>
<p className="text-sm text-muted-foreground">
Front content goes here. On hover, the card flips to reveal the background image.
</p>
</HoverFlipCard>
)
}Props
| Prop | Type | Required | Description |
|---|---|---|---|
| children | ReactNode | Yes | Content rendered on both front and back sides |
| imageBackground | string | Yes | Background image URL for the back side |
| className | string | No | Additional classes for inner content wrapper |
Notes
- Relies on custom 3D utility classes (e.g.
transform-3d,rotate-y-180,backface-hidden). - Image side uses
background-positionthat follows cursor for a subtle parallax effect. - Front and back share the same children for a consistent flip.
Source Code
tsx
'use client'
import { useState, useCallback, useMemo } from 'react'
interface HoverFlipCardProps {
children: React.ReactNode
imageBackground: string
className?: string
}
const HoverFlipCard = ({ children, imageBackground, className }: HoverFlipCardProps) => {
const [position, setPosition] = useState({ x: 50, y: 50 })
const [isHovered, setIsHovered] = useState(false)
const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
const { left, top, width, height } = e.currentTarget.getBoundingClientRect()
const x = ((e.clientX - left) / width) * 100
const y = ((e.clientY - top) / height) * 100
setPosition({ x, y })
}, [])
const memoizedChildren = useMemo(() => children, [children])
return (
<div
className="relative w-80 h-80 sm:w-96 sm:h-96 mx-auto perspective-[1000px]"
onMouseMove={handleMouseMove}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div
className={`relative w-full h-full transition-transform duration-500 transform-3d ${isHovered ? 'rotate-y-180' : ''}`}
>
<div className={`absolute w-full h-full backface-hidden ${className}`}>
{memoizedChildren}
</div>
<div
className={`absolute w-full h-full backface-hidden rotate-y-180 bg-cover bg-center overflow-hidden ${className}`}
style={{
backgroundImage: `url(${imageBackground})`,
backgroundPosition: `${position.x}% ${position.y}%`
}}
>
{memoizedChildren}
</div>
</div>
</div>
)
}
export default HoverFlipCard