Motion UIComponentsComponent

Tilt card

A card that leans toward the pointer in 3D, on compositor transforms only.

npx shadcn@latest add @motion/tilt-card

API reference

Components

<TiltCard />

A tile that leans towards the pointer, with the entrance/tilt split wired. The outer motion.div runs the item entrance variant; the inner element (a motion.div, or a motion.a when href is set) runs the pointer tilt and carries the surface classes. Drop arbitrary children inside and layer the tile's own micro-interaction on top via onHoverStart (div) or whileHover / whileFocus (anchor).

item

Variants

The entrance variant for the OUTER element (opacity / y / scale). Kept off the tilting inner element so the entrance and the tilt never both write transform. Drive it from a parent whileInView orchestration.

interactive

boolean

Whether the tilt (and the inner micro-interaction hooks) are live. Pass false under reduced motion and the card renders flat and static.

maxTilt

number

The maximum pointer-tilt swing, in degrees. This is motion SHAPE, not feel, so it is a prop; the smaller a card, the larger this usually is for the swing to read equally alive.

outerClassName?

string

Merged onto the OUTER element (the one the entrance variant animates) - grid placement (col-span, row-span) lives here.

className?

string

Merged onto the INNER tilting element - surface treatment (bg-card, border, padding, radius) lives here.

href?

string

When set, the inner element is a real <a href> (a whole tile that is a link). Omit for a plain <div> surface.

target?

string

Anchor target, e.g. _blank. Only meaningful with href.

rel?

string

Anchor rel, e.g. noreferrer. Only meaningful with href.

onHoverStart?

() => void

Hover-start callback for the <div> form (used for a hover tick). Wired only when interactive. Ignored when href is set.

whileHover?

string

When set, the inner <a> drives descendant ArrowNudge instances on hover and keyboard focus via useArrowNudgeActive. Only meaningful with href; wired only when interactive.

whileFocus?

string

Reserved for API symmetry with anchor gesture wiring. Pass the same label as whileHover when keyboard focus should also nudge the arrow.

children

ReactNode

The card's content.

Hooks

usePointerTilt

The pointer-tilt mechanic as a hook, for composing the tilt onto an element you already own. Returns a ref, a style (the two rotation springs plus a perspective) and the two pointer handlers; spread style and the handlers onto a Motion element and attach ref, and the surface leans towards the pointer up to maxTilt degrees. Compositor transforms only. When interactive is false everything is undefined, so the element stays flat. No static will-change: transform is set: Motion promotes the layer for the duration of an active transform animation and clears it after, so baking a permanent hint onto every tile only adds standing GPU-memory pressure for no benefit. The tilt is a compositor transform either way.

usePointerTilt(maxTilt: number, interactive: boolean): PointerTilt

Returns

ref

(element: HTMLElement | null) => void

Callback ref for the tilting element - the pointer maths measure the pointer's position against this element's box.

style

MotionStyle | undefined

The rotateX / rotateY spring values plus a fixed perspective, ready to spread onto the element's style. undefined when not interactive.

onPointerMove

((event: React.PointerEvent<HTMLElement>) => void) | undefined

Pointer-move handler that maps the pointer position to the tilt. undefined when not interactive.

onPointerLeave

(() => void) | undefined

Pointer-leave handler that springs the tilt back to flat. undefined when not interactive.

Related examples