Motion UIComponentsComponent

Coverflow

A 3D coverflow carousel where items rotate, scale and fade as they leave centre.

npx shadcn@latest add @motion/coverflow

API reference

Components

<CoverflowItem />

Wraps a consumer's card and applies the coverflow projection: the outer element is the motion-plus ticker item (carrying its measured layout), the inner element rides the x/rotateY/scale/opacity/zIndex MotionValues from useCoverflowItem. Drop one per slide into Coverflow's items. The card inside stays static - all the motion is the projection here.

children?

ReactNode

Your card markup - the thing the projection is applied to. It is sized to the geometry's cardWidth; give it w-full so it fills the slot.

label?

string

Accessible label for the slide (e.g. "Testimonial 2 of 6"). The item carries role="group" + aria-roledescription="slide".

className?

string

Merged onto the projected wrapper, for consumer-context layout.

<CoverflowControls />

The coverflow's navigation strip: reads the live carousel page state from useCarousel(), renders the shared CarouselControls (prev/next arrows + selectable "dot"-variant tablist + screen-reader announcer), and mirrors the carousel's paging into Coverflow's keyboard bridge so arrow / Home / End keys work on the focused region. Render it as a child of Coverflow.

announce?

(index: number, total: number) => string

Builds the aria-live announcement for the current slide, given its 0-based index and the total. Omit to suppress the announcer (e.g. when the section announces position elsewhere).

prevLabel?

string = "Previous slide"

Accessible label for the previous arrow. Defaults to "Previous slide".

nextLabel?

string = "Next slide"

Accessible label for the next arrow. Defaults to "Next slide".

dotsLabel?

string = "Choose a slide"

aria-label for the dots tablist. Defaults to "Choose a slide".

className?

string

Merged onto the control-strip root.

<Coverflow />

The coverflow stage: a wide mask band that the receding neighbours peek into and fade out of, wrapping a single-card-width Carousel track. Provides the projection geometry to its items (each a CoverflowItem) via context, and owns the arrow / Home / End keyboard handling on the focusable region. Paging rides the theme's "gentle" transition; only the geometry (where the cards travel) is a prop.

items

ReactNode[]

One CoverflowItem (wrapping your card) per slide.

cardWidth?

number = 340

The centred card's width in px. Defaults to 340.

rotation?

number = 22

Peak neighbour rotateY in degrees. Defaults to 22.

neighbourScale?

number = 0.82

Scale a fully-receded neighbour shrinks to. Defaults to 0.82.

neighbourOpacity?

number = 0.45

Opacity a fully-receded neighbour fades to. Defaults to 0.45.

perspective?

number = 1200

Card transformPerspective in px. Defaults to 1200.

startIndex?

number = 0

The slide shown on mount (0-based). Defaults to 0.

"aria-label"?

string

aria-label for the carousel region. Defaults to "Carousel, use the arrow keys to navigate".

children?

ReactNode

The controls, typically a single CoverflowControls. Rendered as a sibling below the stage inside the mask band.

className?

string

Merged onto the mask band root.

Hooks

useCoverflowItem

Derives the coverflow projection for the current ticker item from its offset MotionValue and the parent Coverflow's geometry. Call it inside a Coverflow's items (it reads the ticker item + geometry contexts); use it only when you need the raw values to build a bespoke projected element - otherwise reach for CoverflowItem, which applies them for you.

useCoverflowItem(): CoverflowItemValues

Returns

offset

MotionValue<number>

The raw per-item ticker offset the projection is derived from.

x

MotionValue<string>

Horizontal travel as a percentage string ("0%" centred, "120%"/"-120%" off-stage), so cards fly out sideways rather than crowding the centre.

rotateY

MotionValue<number>

The 3D swing in degrees (apply flat ? 0 : rotateY yourself).

scale

MotionValue<number>

The recede scale.

opacity

MotionValue<number>

The recede opacity.

zIndex

MotionValue<number>

Stacking order, highest at centre, so the active card sits on top.

geometry

CoverflowGeometry

The resolved CoverflowGeometry from the parent Coverflow.

props

ReturnType<typeof useTickerItem>["props"]

The motion-plus ticker item props (className / style / aria) to spread onto the outer list element.