Skip to content

Tilt Card

A 3D tilt wrapper that applies a perspective tilt effect on mouse movement. Inspired by Motion Primitives’ Tilt. Wrap any Card or element for interactive depth.

Terminal window
npx shadcn@latest add https://joaocarmassi.com/components/tilt.json
import { Tilt } from '@/components/ui/tilt';

Tilt Card
Hover to see the 3D tilt effect.
Wrap any element with the Tilt component for interactive depth.
<Tilt rotationFactor={15} springOptions={{ stiffness: 300, damping: 20 }}>
<Card className='w-72'>
<CardHeader>
<CardTitle>Tilt Card</CardTitle>
<CardDescription>Hover to see the 3D tilt effect.</CardDescription>
</CardHeader>
<CardContent>
<p className='text-sm text-muted-foreground'>
Wrap any element with the Tilt component for interactive depth.
</p>
</CardContent>
</Card>
</Tilt>

Reversed
Tilt moves opposite to cursor.
Use isReverse to invert the tilt direction.
<Tilt isReverse rotationFactor={20}>
<Card>...</Card>
</Tilt>

Prop Type Default Description
children React.ReactNode Content to tilt.
className string Additional CSS classes.
style MotionStyle Motion style overrides.
rotationFactor number 15 Max rotation angle in degrees.
isReverse boolean false Reverse the tilt direction.
springOptions SpringOptions Spring animation config.

A companion component that places an image inside the Tilt card and gives it real parallax depth. A blurry, desaturated background layer and a sharp foreground layer shift at different rates as the card tilts, creating the illusion that the image has volume.

Terminal window
npx shadcn@latest add https://joaocarmassi.com/components/depth-media.json
import { DepthMedia } from '@/components/ui/depth-media';

Place DepthMedia inside a positioned container (e.g. relative h-48) within a Tilt card and hover to see the parallax layers in action.

Mountain landscape
Depth Media
Hover to feel the parallax depth.
<Tilt rotationFactor={15} springOptions={{ stiffness: 300, damping: 20 }}>
<Card className='w-72 overflow-hidden'>
<div className='relative h-48'>
<DepthMedia src='https://picsum.photos/288/192' alt='...' />
</div>
<CardHeader>
<CardTitle>Depth Media</CardTitle>
<CardDescription>Hover to feel the parallax depth.</CardDescription>
</CardHeader>
</Card>
</Tilt>

Increase depthIntensity for more pronounced layer separation.

Low intensity

depthIntensity={4}

High intensity

depthIntensity={16}

{
/* Subtle */
}
<DepthMedia src='...' depthIntensity={4} />;
{
/* Dramatic */
}
<DepthMedia src='...' depthIntensity={16} />;
Prop Type Default Description
src string Image URL passed to the parallax image.
alt string '' Accessible alt text for the foreground image.
className string Additional CSS classes for the container.
depthIntensity number 8 Parallax multiplier — higher values increase layer shift.
...img props Omit<ComponentProps<'img'>, 'src' | 'alt' | 'className'> Forwarded to the <img> (loading, sizes, decoding, etc.).

You can pass regular <img> props directly to DepthMedia.

<DepthMedia
src='https://picsum.photos/288/192'
alt='Mountain landscape'
sizes='(max-width: 768px) 100vw, 288px'
loading='lazy'
decoding='async'
/>