Skip to content

Draggable Card Stack

Swipe through a stack of cards with a spring-like return.

LIVE PREVIEWInteract with the component
Just React. No extra dependencies.

Copy the component and its CSS module into your project.

CSS Modules

Usage

Matches your current playground settings.

jsx
import DraggableCardStack from "./DraggableCardStack";
export default function Example() {
return (
<DraggableCardStack
items={[
{
"id": "dune",
"title": "Dune studies",
"description": "01 / Shapes in the sand",
"image": "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%20700%22%3E%3Cdefs%3E%3Cfilter%20id%3D%22n%22%3E%3CfeTurbulence%20type%3D%22fractalNoise%22%20baseFrequency%3D%22.7%22%20numOctaves%3D%223%22%20stitchTiles%3D%22stitch%22%2F%3E%3C%2Ffilter%3E%3C%2Fdefs%3E%3Cpath%20fill%3D%22%23d8ad8c%22%20d%3D%22M0%200h600v700H0z%22%2F%3E%3Ccircle%20cx%3D%22390%22%20cy%3D%22190%22%20r%3D%2290%22%20fill%3D%22%23f9d7ae%22%2F%3E%3Cpath%20d%3D%22M0%20450Q200%20190%20600%20410V700H0Z%22%20fill%3D%22%23b78461%22%2F%3E%3Cpath%20d%3D%22M0%20520Q380%20300%20600%20490V700H0Z%22%20fill%3D%22%23805848%22%2F%3E%3Cpath%20d%3D%22M0%20650Q250%20400%20600%20620V700H0Z%22%20fill%3D%22%234b3935%22%2F%3E%3Cpath%20d%3D%22M0%200h600v700H0z%22%20filter%3D%22url(%23n)%22%20opacity%3D%22.085%22%2F%3E%3C%2Fsvg%3E",
"color": "#fff3e8"
},
{
"id": "tide",
"title": "In the current",
"description": "02 / A study in motion",
"image": "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%20700%22%3E%3Cdefs%3E%3Cfilter%20id%3D%22n%22%3E%3CfeTurbulence%20type%3D%22fractalNoise%22%20baseFrequency%3D%22.7%22%20numOctaves%3D%223%22%20stitchTiles%3D%22stitch%22%2F%3E%3C%2Ffilter%3E%3C%2Fdefs%3E%3Cpath%20fill%3D%22%23234652%22%20d%3D%22M0%200h600v700H0z%22%2F%3E%3Cpath%20d%3D%22M-50%20560C120%2020%20300%20750%20670%20120%22%20fill%3D%22none%22%20stroke%3D%22%23648b90%22%20stroke-width%3D%22250%22%2F%3E%3Cpath%20d%3D%22M-50%20560C120%2020%20300%20750%20670%20120%22%20fill%3D%22none%22%20stroke%3D%22%2394b2af%22%20stroke-width%3D%22155%22%2F%3E%3Cpath%20d%3D%22M-50%20560C120%2020%20300%20750%20670%20120%22%20fill%3D%22none%22%20stroke%3D%22%23c1d0c4%22%20stroke-width%3D%2265%22%2F%3E%3Cpath%20d%3D%22M0%200h600v700H0z%22%20filter%3D%22url(%23n)%22%20opacity%3D%22.085%22%2F%3E%3C%2Fsvg%3E",
"color": "#f1fcf4"
},
{
"id": "bloom",
"title": "Full bloom",
"description": "03 / Colour in balance",
"image": "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%20700%22%3E%3Cdefs%3E%3Cfilter%20id%3D%22n%22%3E%3CfeTurbulence%20type%3D%22fractalNoise%22%20baseFrequency%3D%22.7%22%20numOctaves%3D%223%22%20stitchTiles%3D%22stitch%22%2F%3E%3C%2Ffilter%3E%3C%2Fdefs%3E%3Cpath%20fill%3D%22%23a35574%22%20d%3D%22M0%200h600v700H0z%22%2F%3E%3Ccircle%20cx%3D%22300%22%20cy%3D%22350%22%20r%3D%22205%22%20fill%3D%22%23c77488%22%2F%3E%3Cellipse%20cx%3D%22300%22%20cy%3D%22350%22%20rx%3D%2270%22%20ry%3D%22230%22%20fill%3D%22%23f4c4bd%22%20transform%3D%22rotate(-40%20300%20350)%22%2F%3E%3Cellipse%20cx%3D%22300%22%20cy%3D%22350%22%20rx%3D%2270%22%20ry%3D%22230%22%20fill%3D%22%23e99b9f%22%20transform%3D%22rotate(40%20300%20350)%22%2F%3E%3Ccircle%20cx%3D%22300%22%20cy%3D%22350%22%20r%3D%2262%22%20fill%3D%22%2363344e%22%2F%3E%3Cpath%20d%3D%22M0%200h600v700H0z%22%20filter%3D%22url(%23n)%22%20opacity%3D%22.085%22%2F%3E%3C%2Fsvg%3E",
"color": "#fff2f0"
}
]}
cardHeight={320}
swipeThreshold={80}
rotation={10}
loop={true}
/>
);
}

Using Draggable Card Stack

Supply an ordered collection through items and give the stack enough height for its cards. swipeThreshold controls how far a drag must travel before advancing.

Use activeIndex and onIndexChange to synchronise another control. Set loop according to whether reaching the end should wrap to the first card.

Props

NameTypeRequiredDefaultDescription
itemsobject[]No[]Array of { id, content?, title?, description?, image?, background?, color? }. content accepts any React node.
activeIndexnumberNoundefinedOptional controlled card index.
defaultIndexnumberNo0Initial card index when uncontrolled.
onIndexChangefunctionNoundefinedCalled with the next index after a swipe or navigation action.
cardHeightnumberNo300Card height in pixels.
swipeThresholdnumberNo80Horizontal travel required to change cards, in pixels.
rotationnumberNo10Rotation of the stacked cards and exit animation, in degrees.
loopbooleanNotrueWrap from the last card to the first.
labelstringNo"Card stack"Accessible group label.
classNamestringNo""Additional CSS classes, merged with the component styles.
styleCSSPropertiesNoundefinedInline styles for caller-defined sizing and visual overrides.

Related components

A paper flap lifts in 3D as a message slides out of its envelope.

A lightly textured receipt pairs precise item rows with a torn paper edge.

A suspended badge swings naturally after you drag and release its handle.

Fine metallic grooves catch a shifting iridescent pointer highlight.