Motion UIComponentsComponent

Card stack

A fanned card stack navigated with carousel controls and a top-left pop between cards.

npx shadcn@latest add @motion/card-stack

API reference

Components

<CardStackCard />

One card in the stack. Reads its position from the CardStack context and renders a compositor-only transform layer carrying the resting depth fan (y offset, scale, fan rotation, opacity) that re-animates whenever the card's depth changes. Buried cards are aria-hidden and pointer-inert. Must be rendered inside a CardStack.

children?

ReactNode

The card face to fan - typically a TestimonialCard or any elevated bg-card block. The mechanic supplies the movement; this is the content that moves.

className?

string

Merged onto the card face. Positioning (the absolute depth fan) is owned by the mechanic; use this for extra per-card styling only.

<CardStack />

The stack stage. Renders the keyboard-reachable carousel group (Arrow keys step it), resolves the fan geometry + reduced-motion mode + the shuffle/return transitions, and provides all of it to every CardStackCard through context. Drop CardStackCard children inside; the stage fans them, they supply their own faces.

state

CardStackHandle

The card-stack controller from useCardStack - the index and transition direction the cards resolve against. Share it with your own navigation controls so one state drives both.

children?

ReactNode

The CardStackCard children, one per card, in a stable order (buried cards stay mounted so the fan keeps its depth).

visibleBehind?

number = 2

How many cards behind the front stay visible in the fan before the rest fade out. Defaults to 2.

cardGap?

number = 16

Vertical push, in px, applied per step back in the fan. Defaults to 16.

cardScaleStep?

number = 0.05

Scale shed per step back in the fan (0.05 = 5% smaller each). Defaults to 0.05.

fanRotation?

number = 4

Degrees of fan rotation applied per step back. Defaults to 4.

cardMinHeight?

string = "19rem"

The front card's min-height, used to size the stage tall enough for the fan to sink into. Match the min-height you give your card face. Defaults to "19rem".

label?

string = "Card stack"

aria-label for the carousel group. Defaults to "Card stack".

className?

string

Merged onto the stack group element.

Hooks

useCardStack

The headless card-stack controller. Owns the front-card index and the last-movement direction and nothing visual, so a consumer wires the same state into CardStack and their own prev/next controls. The index wraps, so the stack loops in both directions.

useCardStack(options: { total: number }): CardStackHandle

Returns

index

number

The current front-card index (0-based), wrapped into [0, total).

direction

number

The direction of the last move: 1 forward, -1 backward. It controls the exit/return path while the fan stays fixed.

movement

number

Monotonically increasing movement count. Used by the visual stack to distinguish its initial resting render from a navigation transition.

total

number

The total number of cards - the wrap modulus.

advance

(dir: number) => void

Advance the stack by dir (1 next, -1 previous), recording the direction for the exit/return path, and wrapping the index.

next

() => void

Advance to the next card (advance(1)).

prev

() => void

Return to the previous card (advance(-1)).

Related examples