Swipe Cards
A Tinder-style swipe card stack that can be dragged, flies out by velocity, and offers programmatic left/right swipes and an empty state.
npx shadcn@latest add https://webberui.com/r/swipe-cards.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<SwipeCards />
Installation
npx shadcn@latest add https://webberui.com/r/swipe-cards.jsonOr, once registries are configured in components.json, install it as @webberui/swipe-cards.
Usage
import * as React from "react";
import {
SwipeCards,
type SwipeCardsHandle,
} from "@/components/ui/swipe-cards";
const ref = React.useRef<SwipeCardsHandle>(null);
<SwipeCards
ref={ref}
cards={[
{ id: 1, content: <div className="h-52 rounded-2xl bg-sky-500" /> },
{ id: 2, content: <div className="h-52 rounded-2xl bg-rose-500" /> },
]}
onSwipe={(id, dir) => console.log(id, dir)}
className="h-60 w-72"
/>;
// Programmatic left/right swipes
<button onClick={() => ref.current?.swipe("left")}>Skip</button>
<button onClick={() => ref.current?.swipe("right")}>Like</button>The topmost card can be dragged left or right with the mouse or by touch, and the x offset maps live to a rotation angle and a "like / skip" cue. On release, if the offset exceeds threshold or the flick velocity exceeds velocityThreshold, the card flies out in that direction and reveals the next one. Once everything has been swiped, emptyState is shown.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
cards | { id: string | number; content: ReactNode }[] | — | The array of cards; the first item sits on top |
onSwipe | (id, dir: "left" | "right") => void | — | Fires when a card is swiped away |
emptyState | ReactNode | default message | The content shown once everything has been swiped |
threshold | number | 100 | Horizontal offset threshold that triggers the fly-out (px) |
velocityThreshold | number | 500 | Flick velocity threshold that triggers the fly-out (px/s) |
className | string | — | Stack container styling (usually where you set the height) |
Ref (SwipeCardsHandle)
| Method | Type | Description |
|---|---|---|
swipe | (dir: "left" | "right") => void | Programmatically swipes away the topmost card, useful for custom buttons |
How it works
- The cards behind shrink and shift down layer by layer with depth, exposing their edges to create the sense of a stack; at most 3 layers are rendered at once.
- The drag uses
drag="x", withuseTransformmapping the offset to a rotation angle; the fly-out uses a spring, and the index only updates after the exit so the next card moves up. - Set the container height through
className(h-60, say), and let the card content decide its own width and height; the container does not clip, so the flying card can exit completely.
Accessibility
- The topmost card is focusable (
tabIndex=0) and supports → to like and ← to skip, carryingrole="group"and anaria-labelexplaining the interaction. - The cards behind are hidden from assistive technology with
aria-hiddenand havepointer-events: none, so only the topmost one is interactive. - When the user has "reduce motion" enabled at the system level, the drag and the rotation are disabled, switching instead to a fade out triggered by button or keyboard.
Perspective Carousel
A 3D perspective carousel (coverflow style) with drag to page, looping, autoplay, and controlled and uncontrolled modes.
Cover Carousel
A horizontal carousel of cover cards whose indicator is driven continuously (scrubbed) by the scroll ratio, with full keyboard navigation and ARIA.