--- url: /guide/getting-started/overview.md --- # Overview `@react-native-motion-kit/swipe-deck` is a high-performance Tinder-style swipe deck for React Native. It is built for apps that need a smooth card stack, programmatic like/pass buttons, progress-driven overlays, and a typed API that does not make every screen wire a controller by hand. ## Mental Model The main API is a typed compound component family: - `createSwipeDeck()` creates one deck family for an item type. - `Root` owns data, gesture state, motion options, actions, events, and layout. - `Card` renders each mounted item in the bounded forward window. - Factory hooks read state, run actions, subscribe to events, and expose Reanimated shared values for animated UI. ```tsx import { createSwipeDeck } from '@react-native-motion-kit/swipe-deck'; type Profile = { id: string; name: string; }; const ProfileDeck = createSwipeDeck(); ``` Create the factory once, export it from a shared module, and use hooks from that same factory around a matching `Root`. ## Why It Exists - The deck does not render the full data set. - Promoted cards keep item identity through stable keys. - Manual drag motion and programmatic action motion can be tuned separately. - Interaction shared values update on the UI thread without rerendering React every gesture frame. - Events describe committed model changes such as swipes, undo, index changes, and end reached. ## Core Concepts ### Bounded Forward Window By default, the deck mounts up to three cards from the active index forward: 1. current card 2. next card 3. next buffered card This keeps the stack continuous without backfilling dismissed cards. Use `visibleCardCount` to tune that maximum budget. ### Stable Item Keys `getKey` is required. The same logical item must return the same key across swipes, and different items should not share a key. The deck uses that key as the React identity for each mounted card. ### Committed Events vs Live Interaction Use event hooks for committed model changes. Use interaction shared values for frame-synchronous visual feedback while a card is dragging, dismissing, or restoring. --- url: /guide/getting-started/installation.md --- # Installation Install the package and its React Native gesture/animation peer dependencies: ```sh npm install @react-native-motion-kit/swipe-deck react-native-gesture-handler react-native-reanimated react-native-worklets ``` You can use your package manager of choice. For Yarn: ```sh yarn add @react-native-motion-kit/swipe-deck react-native-gesture-handler react-native-reanimated react-native-worklets ``` ## Minimum Versions | Package | Minimum | | ------------------------------ | -------- | | `react` | `18.0.0` | | `react-native` | `0.75.0` | | `react-native-gesture-handler` | `2.24.0` | | `react-native-reanimated` | `4.0.0` | | `react-native-worklets` | `0.5.0` | ## React Native Setup Follow the Reanimated and Worklets setup instructions for your React Native or Expo version. In Babel config, make sure `react-native-worklets/plugin` is the last Babel plugin. ```js title="babel.config.js" module.exports = { presets: ['module:@react-native/babel-preset'], plugins: ['react-native-worklets/plugin'], }; ``` Gesture Handler also expects your app surface to be under `GestureHandlerRootView`. ```tsx import { GestureHandlerRootView } from 'react-native-gesture-handler'; export function AppRoot() { return {/* app */}; } ``` --- url: /guide/getting-started/quick-start.md --- # Quick Start Create one typed deck family for your item type. `Root`, `Card`, hooks, actions, and events from that factory share the same registry namespace. ```tsx import { Text, View } from 'react-native'; import { createSwipeDeck, SwipeDeckMotion } from '@react-native-motion-kit/swipe-deck'; type Profile = { id: string; name: string; bio: string; }; const ProfileDeck = createSwipeDeck({ motion: SwipeDeckMotion.tinder({ drag: { mode: 'horizontal', liftYFactor: 0.15, }, rotation: { mode: 'grab-position', }, dismiss: { threshold: ({ width }) => width * 0.3, velocityThreshold: 800, minDuration: 300, maxDuration: 520, }, }), }); function ProfileDeckEvents() { ProfileDeck.useDeckEventListener('swipe', ({ item, direction, source }) => { console.log(item.name, direction, source); }); ProfileDeck.useDeckEventListener('endReached', () => { console.log('No more cards'); }); return null; } function ProfileCard({ profile, active }: { profile: Profile; active: boolean }) { return ( {active ? 'Now viewing' : 'Up next'} {profile.name} {profile.bio} ); } export function ProfileDeckScreen({ profiles }: { profiles: Profile[] }) { return ( <> item.id} visibleCardCount={3}> {({ item, isActive }) => } ); } ``` ## Add Controls Use state and action hooks for React-rendered UI such as counters and buttons. ```tsx function ProfileDeckControls() { const { activeIndex, count, canSwipe, canUndo, isCompleted } = ProfileDeck.useDeckState(); const { swipeLeft, swipeRight, undo } = ProfileDeck.useDeckActions(); const current = activeIndex >= 0 ? activeIndex + 1 : 0; return ( {isCompleted ? 'Done' : `${current} / ${count}`} Nope Undo Like ); } ``` Enable undo tracking only when you expose undo UX: ```tsx item.id} undoEnabled> {({ item }) => } ``` --- url: /guide/getting-started/ai.md --- # AI Usage Guide When generating code with an AI assistant, give it these rules. They prevent the most common integration mistakes. ## LLM-Readable Docs This site publishes Markdown files for AI tools during `rspress build`. - [`/llms.txt`](/llms.txt): compact documentation index with page links and summaries. Use this when the agent should choose only the relevant pages. - [`/llms-full.txt`](/llms-full.txt): full English documentation in one Markdown file. Use this when the agent has enough context budget and should understand the whole API surface. - [`/ko/llms.txt`](/ko/llms.txt) and [`/ko/llms-full.txt`](/ko/llms-full.txt): Korean equivalents. Before the public docs URL is available, build locally and read the generated files from `docs/doc_build/`. ## Recommended Prompt ```md Use @react-native-motion-kit/swipe-deck. - Create one createSwipeDeck() factory outside render. - Render Root and Card from that same factory. - Use a stable getKey(item) value, usually item.id. - Use factory hooks from the same factory instance as the Root. - Use useDeckState/useDeckActions for React UI. - Use useDeckInteraction for Reanimated UI-thread overlays. - Use useDeckEvent/useDeckEventListener for committed swipe, undo, indexChange, and endReached events. - Add undoEnabled only when the UI exposes undo. - Do not derive deck id from item ids, timestamps, or values that change each render. ``` ## Good Factory Shape ```tsx // profile-deck.ts import { createSwipeDeck } from '@react-native-motion-kit/swipe-deck'; export type Profile = { id: string; name: string; }; export const ProfileDeck = createSwipeDeck(); ``` ```tsx // ProfileDeckScreen.tsx import { ProfileDeck } from './profile-deck'; export function ProfileDeckScreen({ profiles }: { profiles: Profile[] }) { return ( item.id}> {({ item }) => } ); } ``` ## Common Mistakes To Avoid - Do not call `createSwipeDeck()` inside a component render path. - Do not use hooks from one factory to control a Root from another factory. - Do not use array indexes as keys when items can be inserted, removed, or reordered. - Do not use `useDeckInteraction` for committed business logic; use events for committed model changes. - Do not turn on `undoEnabled` unless the deck exposes undo controls. --- url: /guide/usage/basic-usage.md --- # Basic Usage ## Factory API The factory API is the primary API. Use it when a deck needs hooks, actions, events, interaction shared values, or multiple named instances. ```tsx import { createSwipeDeck } from '@react-native-motion-kit/swipe-deck'; const ProfileDeck = createSwipeDeck(); function Screen() { return ( item.id}> {({ item }) => } ); } ``` The factory keeps `Root`, `Card`, and hooks on the same item type without repeating generics in JSX. ## Card Render Info `Card` receives render info for each mounted item. | Field | Meaning | | ---------- | ------------------------------------------------------- | | `item` | The item from your `data` array. | | `index` | The item index in `data`. | | `offset` | The card offset from the active item. | | `role` | `'current'` for the active card, `'next'` for buffered. | | `isActive` | `true` only for the active card. | ```tsx {({ item, role, isActive }) => } ``` ## Allowed Directions `allowedDirections` limits which directions can be accepted as a dismiss. ```tsx item.id} allowedDirections={['right']}> {({ item }) => } ``` - Omit `allowedDirections` to allow both directions. - Pass `['left']` or `['right']` to allow only one dismiss direction. - Pass `[]` to allow dragging but reject every dismiss release and programmatic swipe action. ## Static API Use the static API only when you need inline card rendering and do not need factory hooks. ```tsx import { SwipeDeck } from '@react-native-motion-kit/swipe-deck'; function InlineDeck() { return ( item.id}> >{({ item }) => } ); } ``` Static `Root` does not accept `id`, and static `Root` / `Card` do not share a factory type. Pass the item type to `Card` when you want typed render props. --- url: /guide/usage/deck-hooks.md --- # Deck Hooks Factory hooks expose deck state, actions, and interaction values without a provider prop or controller object. ```tsx const ProfileDeck = createSwipeDeck(); ``` ## `useDeckState(id?)` Returns React-rendered deck state. | Field | Meaning | | ------------- | --------------------------------------------------------- | | `activeIndex` | Current active item index, or `-1` before attach. | | `count` | Total item count in the attached deck. | | `isCompleted` | Whether the deck has consumed all items. | | `canSwipe` | Whether a dismiss action can currently be accepted. | | `canUndo` | Whether the latest valid swipe can currently be restored. | ```tsx function Counter() { const { activeIndex, count, isCompleted } = ProfileDeck.useDeckState(); const current = activeIndex >= 0 ? activeIndex + 1 : 0; return {isCompleted ? 'Done' : `${current} / ${count}`}; } ``` Derive the current item from your own `data[activeIndex]` when needed. Deck state stays primitive and stable. ## `useDeckActions(id?)` Returns stable action callbacks. ```tsx function Controls() { const { canSwipe, canUndo } = ProfileDeck.useDeckState(); const { swipeLeft, swipeRight, undo } = ProfileDeck.useDeckActions(); return ( Nope Undo Like ); } ``` `swipeLeft()` and `swipeRight()` return `true` when accepted and `false` when the deck is unattached, disabled, animating, unmeasured, completed, or the direction is not allowed. `undo()` returns `true` when `canUndo` is true, including after the deck is completed. ## `useDeckInteraction(id?)` Returns Reanimated shared values for progress-driven UI. ```tsx function SwipeReactionOverlay() { const { signedProgress } = ProfileDeck.useDeckInteraction(); const likeStyle = useAnimatedStyle(() => { const progress = Math.max(signedProgress.get(), 0); return { opacity: progress, transform: [{ scale: 0.9 + progress * 0.18 }], }; }); return ; } ``` Interaction values update on the UI thread and do not rerender React every gesture frame. | Value | Meaning | | ------------------ | ------------------------------------------------------ | | `progress` | Absolute swipe progress from `0` to `1`. | | `signedProgress` | Signed progress from `-1` to `1`. | | `direction` | Raw live direction signal: `-1`, `0`, or `1`. | | `dismissDirection` | Accepted dismiss side: `'left'`, `'right'`, or `null`. | | `translationX` | Active card horizontal translation. | | `translationY` | Active card vertical translation. | | `isDragging` | Whether the deck is dragging or dismissing. | | `phase` | `idle`, `dragging`, `dismissing`, or `undoing`. | Use `phase` for frame-synchronous visual feedback. Use event hooks for committed state changes. --- url: /guide/usage/handling-events.md --- # Handling Events Event hooks describe committed model events. They are separate from live interaction values. ## Event Map | Event | Payload | | ------------- | ------------------------------------ | | `swipe` | `{ item, index, direction, source }` | | `undo` | `{ item, index, direction }` | | `indexChange` | `{ index }` | | `endReached` | `true` | `swipe.source` is `'gesture'` when the user releases a pan gesture past the configured threshold or velocity policy. It is `'programmatic'` when the swipe commits through `actions.swipeLeft()` or `actions.swipeRight()`. ## `useDeckEvent` `useDeckEvent(eventName, initialValue?, id?)` returns the latest committed event snapshot for React-rendered UI. ```tsx function ProfileDeckStatus() { const lastSwipe = ProfileDeck.useDeckEvent('swipe', null); const endReached = ProfileDeck.useDeckEvent('endReached', false); return ( {endReached ? 'Done' : lastSwipe ? `Last swipe: ${lastSwipe.direction} from ${lastSwipe.source}` : 'No swipe yet'} ); } ``` This is a latest-value API, not an event history. Event snapshots clear when a Root attaches or detaches. Initial values are intentionally restricted to the event payload shape, `null`, `undefined`, or `false` for `endReached`. Use `null` for object events such as `swipe`. For a named deck without an initial value, pass the id as the second argument: ```tsx const lastSwipe = ProfileDeck.useDeckEvent('swipe', 'nearby'); ``` If you also pass an initial value, id is the third argument. ## `useDeckEventListener` `useDeckEventListener(eventName, listener, id?)` subscribes imperatively without forcing React state into your app code. ```tsx function ProfileDeckEvents() { ProfileDeck.useDeckEventListener('swipe', ({ item, direction, source }) => { console.log(item, direction, source); }); ProfileDeck.useDeckEventListener('undo', ({ item }) => { console.log('Restored', item); }); ProfileDeck.useDeckEventListener('endReached', () => { console.log('No more cards'); }); return null; } ``` Successful swipe events emit in `swipe -> indexChange -> endReached` order. Undo emits `undo -> indexChange`. --- url: /guide/usage/programmatic-actions.md --- # Programmatic Actions Programmatic actions come from `useDeckActions()`. Use them for buttons, external controls, or any app logic that should dismiss the active card. ```tsx function LikeButton() { const { swipeRight } = ProfileDeck.useDeckActions(); return ( Like ); } ``` ## Action Motion `motion` controls manual drag feel. `actionMotion` controls only programmatic actions such as `swipeLeft()` and `swipeRight()`. ```tsx import { createSwipeDeck, SwipeDeckActionMotion, SwipeDeckMotion, } from '@react-native-motion-kit/swipe-deck'; const ProfileDeck = createSwipeDeck({ motion: SwipeDeckMotion.tinder(), actionMotion: SwipeDeckActionMotion.springboard({ anticipationDistance: ({ width }) => width * 0.04, anticipationDuration: 80, dismissDuration: 320, }), }); ``` ## Recipes ### `SwipeDeckActionMotion.direct(options?)` Dismisses immediately toward the action direction. Omitted values reuse the deck's resolved dismiss duration, easing, and offscreen multiplier. ```tsx actions.swipeLeft( SwipeDeckActionMotion.direct({ duration: 180, }), ); ``` ### `SwipeDeckActionMotion.springboard(options?)` Moves a little in the opposite direction first, then dismisses offscreen. During anticipation, swipe progress and live direction stay neutral so opposite-side overlays do not flash. ```tsx const actionMotion = SwipeDeckActionMotion.springboard({ anticipationDistance: 40, anticipationDuration: 160, dismissDuration: 500, }); ``` ## Precedence `actionMotion` is replacement-based, not deep-merged: 1. factory `actionMotion` from `createSwipeDeck({ actionMotion })` 2. `Root actionMotion`, replacing the factory default for that Root 3. per-call recipe passed to `swipeLeft(recipe)` or `swipeRight(recipe)` Actions are callback-safe. If a React Native press event is passed to `swipeRight` or `swipeLeft`, the event argument is ignored and the configured action motion is used. --- url: /guide/usage/undo.md --- # Undo Undo is opt-in. Add `undoEnabled` to a Root when the deck exposes undo or back-swipe UX. ```tsx item.id} undoEnabled> {({ item }) => } ``` When enabled, each successful swipe stores one key/index/direction metadata entry in a LIFO undo stack. Lookups use a key-to-index map for the current `data`, and invalid entries are pruned when data or keys change. When omitted, successful swipes do not store undo metadata, `canUndo` stays `false`, and `actions.undo()` returns `false`. ## Undo Button ```tsx function UndoButton() { const { canUndo } = ProfileDeck.useDeckState(); const { undo } = ProfileDeck.useDeckActions(); return ( Undo ); } ``` ## Undo Motion Use `SwipeDeckUndoMotion` to customize how a restored card enters. ```tsx import { createSwipeDeck, SwipeDeckUndoMotion } from '@react-native-motion-kit/swipe-deck'; const ProfileDeck = createSwipeDeck({ undoMotion: SwipeDeckUndoMotion.spring({ springConfig: { damping: 36, stiffness: 300, mass: 3, }, }), }); ``` Available recipes: - `SwipeDeckUndoMotion.spring(options?)`: restores with Reanimated `withSpring`. - `SwipeDeckUndoMotion.timing(options?)`: restores with deterministic `withTiming`. Both recipes accept: - `from: 'auto' | 'left' | 'right'`: `auto` returns from the original swipe direction. - `entryDistance`: number or layout callback for the offscreen start distance. `timing` also accepts `duration` and `easing`. Its default duration is `0`, so the card is restored immediately unless you opt into custom motion. `spring` accepts `springConfig`. ## Precedence `undoMotion` is replacement-based: 1. factory `undoMotion` from `createSwipeDeck({ undoMotion })` 2. `Root undoMotion`, replacing the factory default for that Root 3. per-call recipe passed to `actions.undo(recipe)` Undo is callback-safe. If a React Native press event is passed to `undo`, the event argument is ignored. --- url: /guide/usage/motion.md --- # Motion `SwipeDeckMotion.tinder()` is the built-in motion preset for Tinder-style card stacks. ```tsx import { SwipeDeckMotion } from '@react-native-motion-kit/swipe-deck'; const motion = SwipeDeckMotion.tinder({ drag: { mode: 'horizontal', liftYFactor: 0.15, }, rotation: { mode: 'grab-position', }, dismiss: { threshold: ({ width }) => width * 0.3, velocityThreshold: 800, minDuration: 300, maxDuration: 520, }, }); ``` ## What Follows Swipe Progress? Buffered next cards animate with swipe progress: - scale moves toward `1` - opacity moves toward `1` - `translateY` moves toward `0` When a swipe commits, the dismissed card exits offscreen, the promoted next card keeps its item identity, and a new future item enters the bounded window. ## Drag Mode `drag.mode` controls how the active card uses finger translation while dragging. | Mode | Active card movement | | ------------ | ---------------------------------------------------------- | | `free` | Follows both horizontal and vertical finger movement. | | `horizontal` | Ignores vertical finger movement and uses horizontal drag. | `drag.liftYFactor` lifts the active card upward by `abs(translationX) * liftYFactor`. ## Rotation `rotation.mode` controls whether the rotation anchor is fixed or resolved from the gesture start position. ### Fixed Rotation Use fixed rotation when every gesture should use the same anchor. | Origin | Feel | | --------------- | ------------------------------- | | `center` | Rotates around the card center. | | `top-center` | The upper edge feels anchored. | | `bottom-center` | The lower edge feels anchored. | ```tsx SwipeDeckMotion.tinder({ rotation: { mode: 'fixed', origin: 'bottom-center', direction: 'default', }, }); ``` Use `direction: 'reverse'` when you want the same fixed anchor with the opposite rotation sign. ### Grab-Position Rotation Grab-position rotation is the default Tinder-like behavior. ```tsx SwipeDeckMotion.tinder({ rotation: { mode: 'grab-position', direction: 'default', maxDegrees: 25, }, }); ``` Upper-half grabs use a top-center anchor with the default rotation sign. Lower-half grabs use a bottom-center anchor with the reverse rotation sign. `direction: 'reverse'` flips that mapping. ## Dismiss Target `dismiss.offscreenMultiplier` controls the successful swipe release target. - Successful swipes always dismiss offscreen. - The default `1.5` sends the card to `clearDistance * 1.5`. - `clearDistance` is resolved at release from the swipe direction, rotation mode, rotation direction, and gesture start position. - Values below `1` normalize to `1`. - If `duration` is omitted, velocity-derived timing is computed from the remaining distance to this target. Most apps can tune only `threshold`, `velocityThreshold`, `duration`, `minDuration`, `maxDuration`, and `easing`. ## Motion Precedence Motion values resolve in this order: 1. factory motion defaults from `createSwipeDeck({ motion })` 2. `Root motion`, partially overriding only the fields it specifies 3. direct root props such as `swipeThreshold` and `velocityThreshold` Factory and Root motion are deep-merged. Changing `rotation.mode` does not reset numeric rotation tuning such as `maxDegrees` or `inputRange`. ## Preset Stability Keep motion presets stable with a module-scope constant or `useMemo`. ```tsx const profileDeckMotion = SwipeDeckMotion.tinder({ rotation: { mode: 'fixed', origin: 'bottom-center', }, dismiss: { threshold: ({ width }) => width * 0.3, }, }); const ProfileDeck = createSwipeDeck({ motion: profileDeckMotion, }); ``` --- url: /guide/usage/multi-instance-management.md --- # Multi-Instance Management Use `id` only when you render multiple roots from the same factory. ```tsx function MultiDeckScreen() { const nearbyState = ProfileDeck.useDeckState('nearby'); return ( <> item.id}> {({ item }) => } item.id}> {({ item }) => } {nearbyState.activeIndex + 1} ); } ``` `id` is a factory-scoped deck namespace, not an item key. Two different factories can both use the default id safely, but two mounted roots from the same factory and same id are invalid. ## Id Rules - Keep ids stable and low-cardinality. - Use screen-level names such as `"nearby"` or `"recommended"`. - Do not derive ids from item ids, timestamps, values that change per render, or one-off route values. - Create factories and ids outside render paths. The registry keeps one store per id for the lifetime of the factory so hooks, actions, and interaction shared values stay stable. ## Same-Factory Rule Hooks, actions, and interactions only connect to Roots created by the same factory instance. ```ts const ProfileDeck = createSwipeDeck(); export const { Root: ProfileDeckRoot, Card: ProfileDeckCard, useDeckState: useProfileDeckState, useDeckActions: useProfileDeckActions, useDeckInteraction: useProfileDeckInteraction, } = ProfileDeck; ``` Calling `createSwipeDeck()` again creates a separate registry namespace even when the item type and id are the same. --- url: /guide/usage/visible-card-budget.md --- # Visible Card Budget `visibleCardCount` controls the maximum number of cards kept mounted from the active card forward. ```tsx function CurrentOnlyDeck() { return ( item.id} visibleCardCount={1}> {/* current card only */} ); } function DefaultDeck() { return ( item.id} visibleCardCount={3}> {/* default budget for smoother next-card promotion */} ); } function DeepStackDeck() { return ( item.id} visibleCardCount={9}> {/* deeper stacked UI */} ); } ``` ## Rules | Input | Mounted cards | | ----------------------------------------------- | ---------------------------------------- | | `visibleCardCount={1}` | Only the current card. | | `visibleCardCount={2}` | Up to two cards when enough data exists. | | `visibleCardCount={20}` with 10 remaining items | At most those 10 remaining items. | | Even values | Kept as the maximum budget. | Values below `1` normalize to `1` when enough forward data exists. The mounted count never exceeds the remaining data from the active index. Keep the budget as small as your UI allows. Increase it only when your design visibly exposes a deeper stack or intentionally needs more cards pre-mounted. --- url: /guide/usage/api-reference.md --- # API Reference ## Main Exports ```ts export { SwipeDeckActionMotion, SwipeDeckMotion, createSwipeDeck, SwipeDeck, SwipeDeckUndoMotion, } from '@react-native-motion-kit/swipe-deck'; ``` [Go to source](https://github.com/react-native-motion-kit/react-native-swipe-deck/blob/main/src/index.tsx) ## `createSwipeDeck(config?)` Creates a typed deck family. ```tsx const ProfileDeck = createSwipeDeck({ motion: SwipeDeckMotion.tinder(), }); ``` The returned instance includes: - `Root` - `Card` - `useDeckState` - `useDeckActions` - `useDeckInteraction` - `useDeckEvent` - `useDeckEventListener` ## `SwipeDeck.Root` Props [Go to source](https://github.com/react-native-motion-kit/react-native-swipe-deck/blob/main/src/types.ts) | Prop | Type | Notes | | ------------------- | ------------------------------------ | ----------------------------------------- | | `id` | `string` | Factory-scoped deck namespace. | | `data` | `readonly T[]` | Ordered items rendered by the deck. | | `getKey` | `(item: T, index: number) => string` | Required stable item key. | | `initialIndex` | `number` | Initial active item index. | | `disabled` | `boolean` | Disables accepted actions and gestures. | | `allowedDirections` | `AllowedDirections` | Limits accepted dismiss directions. | | `swipeThreshold` | `SwipeThreshold` | Root-level dismiss threshold override. | | `velocityThreshold` | `number` | Root-level flick threshold override. | | `motion` | `SwipeDeckMotionPreset` | Gesture-driven motion preset. | | `actionMotion` | `SwipeDeckActionMotionRecipe` | Programmatic swipe motion recipe. | | `undoMotion` | `SwipeDeckUndoMotionRecipe` | Programmatic undo restore motion recipe. | | `undoEnabled` | `boolean` | Enables undo history tracking. | | `visibleCardCount` | `number` | Maximum mounted forward card budget. | | `containerStyle` | `StyleProp` | Style applied to the deck container. | | `children` | `ReactNode` | Must include a matching `SwipeDeck.Card`. | ```ts type AllowedDirections = readonly ('left' | 'right')[]; type SwipeThreshold = number | ((layout: SwipeDeckLayout) => number); ``` Static `SwipeDeck.Root` accepts the same props except `id`. ## `SwipeDeck.Card` Props | Prop | Type | Notes | | ---------- | ----------------------------------------- | --------------------------------------- | | `style` | `StyleProp` | Style applied to the absolute card. | | `children` | `(info: SwipeRenderInfo) => ReactNode` | Renders one item in the bounded window. | `SwipeRenderInfo` includes `item`, `index`, `offset`, `role`, and `isActive`. ## State And Actions ```ts type SwipeDeckState = { activeIndex: number; count: number; isCompleted: boolean; canSwipe: boolean; canUndo: boolean; }; type SwipeDeckActions = { swipeLeft: SwipeDeckAction; swipeRight: SwipeDeckAction; undo: SwipeDeckUndoAction; }; ``` Programmatic actions return `boolean` to tell whether the action was accepted. ## Interaction ```ts type SwipeDeckInteractionPhase = 'idle' | 'dragging' | 'dismissing' | 'undoing'; ``` `useDeckInteraction()` returns Reanimated shared values for `progress`, `signedProgress`, `direction`, `dismissDirection`, `translationX`, `translationY`, `isDragging`, and `phase`. ## Events ```ts type SwipeDeckEventMap = { swipe: SwipeEvent; undo: UndoEvent; indexChange: { index: number }; endReached: true; }; ``` Use `useDeckEvent` for latest event snapshots and `useDeckEventListener` for imperative subscriptions. ## Motion Helpers - `SwipeDeckMotion.tinder(options?)` - `SwipeDeckActionMotion.direct(options?)` - `SwipeDeckActionMotion.springboard(options?)` - `SwipeDeckUndoMotion.spring(options?)` - `SwipeDeckUndoMotion.timing(options?)` For behavior details, prefer the guide pages over treating this reference as an onboarding surface. --- url: /index.md --- # React Native Swipe Deck Tinder-style swipe decks for React Native > High-performance card stacks powered by Reanimated, Worklets, and Gesture Handler [Quick Start](/guide/getting-started/quick-start.html) | [GitHub](https://github.com/react-native-motion-kit/react-native-swipe-deck) ## Features - 🤌 **Gesture-First Deck UX**: Drag, flick, threshold, and direction controls tuned for Tinder-style card stacks. - đŸŽī¸ **High-Performance Animations**: Smooth card motion powered by React Native Reanimated shared values and worklets. - đŸĒŸ **Bounded Render Window**: Mount only the active card and a small forward stack instead of the whole data set. - đŸ§Ŧ **Item-Stable Promotion**: Stable item keys let promoted cards keep their React Native view identity. - 🧠 **Typed Compound API**: Create one typed deck family with Root, Card, hooks, actions, and events. - đŸŽ›ī¸ **External Control API**: Trigger swipeLeft, swipeRight, and undo from buttons or other UI components. - 🎨 **Motion Recipes**: Tune gesture motion, programmatic actions, and undo restores independently. - 🧩 **Multi-Instance Management**: Manage multiple deck roots independently with stable factory-scoped IDs. - â†Šī¸ **Undo Support**: Opt into back-swipe UX with action-safe undo motion and LIFO history. - đŸĒ„ **Easy-to-Use API**: Start with Root and Card, then add hooks only when controls or events need them.