npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-native-swipe-cards-stack

v1.1.1

Published

A highly customizable, performant swipeable cards stack component for React Native.

Downloads

9

Readme

React Native Swipe Cards Stack

NPM Version License TypeScript

A highly customizable, performant swipeable cards stack component for React Native with Tinder-like animations.

✨ Features

  • 🚀 High Performance - Built with React Native's Animated API and optimized for 60fps
  • 🎨 Fully Customizable - Every aspect can be customized to match your design
  • 📱 Multi-directional Swiping - Support for left, right, up, and down swipe gestures
  • 👆 Tap Interactions - Built-in tap handling with customizable visual feedback
  • 🎭 Rich Animations - Smooth rotation, scaling, and opacity animations
  • 🎯 TypeScript Ready - Complete type definitions included
  • 🔧 Zero Dependencies - Built with React Native core components only
  • Accessibility Support - Full accessibility features included
  • 🔄 Backward Compatible - Easy migration from simple implementations
  • 📦 Small Bundle Size - Lightweight with no external dependencies

🚀 Installation

npm install react-native-swipe-cards-stack
# or
yarn add react-native-swipe-cards-stack

📱 Quick Start

import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { SwipeableCardsStack } from 'react-native-swipe-cards-stack';

const App = () => {
  // Data can be any type - strings, numbers, objects, etc.
  const [cards] = useState([
    { name: 'John', age: 25, city: 'New York' },
    { name: 'Jane', age: 30, city: 'London' },
    { name: 'Bob', age: 28, city: 'Tokyo' },
  ]);

  const renderCard = (item, index) => (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text style={{ fontSize: 24, fontWeight: 'bold' }}>{item.name}</Text>
      <Text style={{ fontSize: 18 }}>Age: {item.age}</Text>
      <Text style={{ fontSize: 16, color: 'gray' }}>{item.city}</Text>
    </View>
  );

  return (
    <SwipeableCardsStack
      data={cards}
      renderCard={renderCard}
      onSwipe={(direction, item, index) => console.log(`Swiped ${direction} on item at index ${index}:`, item)}
      onTap={(item, index) => console.log(`Tapped on card at index ${index}:`, item)}
      tapActiveOpacity={0.8}
    />
  );
};

🔑 Card Data Flexibility

The component accepts any data type - your array can contain strings, numbers, objects, or any other type:

// ✅ Simple strings
const stringCards = ['Card 1', 'Card 2', 'Card 3'];

// ✅ Numbers
const numberCards = [1, 2, 3, 4, 5];

// ✅ Simple objects (no specific structure required)
const objectCards = [
  { title: 'Card 1', content: 'Any data structure' },
  { title: 'Card 2', content: 'Works perfectly' },
];

// ✅ Complex objects
const complexCards = [
  { id: 'abc', name: 'John', age: 25, hobbies: ['reading', 'coding'] },
  { id: 'def', name: 'Jane', age: 30, location: { city: 'NY', country: 'USA' } },
];

// ✅ Mixed types (though not recommended for consistency)
const mixedCards = ['string', 42, { name: 'object' }, [1, 2, 3]];

// Use with any data type
<SwipeableCardsStack
  data={stringCards}
  renderCard={(item, index) => (
    <Text>{typeof item === 'string' ? item : JSON.stringify(item)}</Text>
  )}
  onSwipe={(direction, item, index) => 
    console.log(`Swiped ${direction} on item at index ${index}:`, item)
  }
/>

// Use keyExtractor for custom unique identifiers when needed
<SwipeableCardsStack
  data={complexCards}
  keyExtractor={(item, index) => 
    typeof item === 'object' && item.id ? item.id : index.toString()
  }
  renderCard={(item, index) => <YourCardComponent data={item} />}
  onSwipe={(direction, item, index) => 
    console.log(`Swiped ${direction} on ${typeof item} at index ${index}`)
  }
/>

// Or let it use index automatically (default behavior)
<SwipeableCardsStack
  data={numberCards}
  renderCard={(item, index) => <Text>Number: {item}</Text>}
  onSwipe={(direction, item, index) => 
    console.log(`Number ${item} at position ${index} swiped ${direction}`)
  }
/>

🎯 Advanced Usage

Card Tap Interactions

import { SwipeableCardsStack } from 'react-native-swipe-cards-stack';

const InteractiveCards = () => {
  const handleCardTap = (card, index) => {
    console.log(`Card tapped at index ${index}:`, card);
    // Navigate to detail view, show modal, etc.
    // navigation.navigate('CardDetails', { card });
  };

  return (
    <SwipeableCardsStack
      data={cardData}
      renderCard={renderCard}
      onTap={handleCardTap}
      tapActiveOpacity={0.7} // Visual feedback on tap
      onSwipe={(direction, card, index) => {
        console.log(`Swiped ${direction}:`, card);
      }}
    />
  );
};

Custom Icons and Animations

import { SwipeableCardsStack } from 'react-native-swipeable-cards-stack';

const CustomCard = () => {
  const customTickIcon = (
    <View style={{ width: 60, height: 60, backgroundColor: 'green', borderRadius: 30 }}>
      <Text style={{ fontSize: 30, textAlign: 'center' }}>❤️</Text>
    </View>
  );

  return (
    <SwipeableCardsStack
      data={cardData}
      renderCard={renderCard}
      rightSwipeIcon={customTickIcon}
      // rightIconStyle={styles.rightIconStyle} icon container styles eg: {position: 'absolute', top: 10}
      animations={{
        duration: 250,
        rotationEnabled: true,
        scaleEnabled: true,
      }}
      thresholds={{
        horizontal: 100,
        vertical: 150,
        iconDelay: 20,
      }}
  onSwipe={(direction, card, index) => {
    console.log(`Swiped ${direction}:`, card);
    if (direction === 'up') {
      // Handle up swipe - navigate to detail screen, show modal, etc.
      console.log('Up swipe detected:', card);
    }
  }}
/>

Custom Up Swipe Handler

const MyCardsWithDetails = () => {
  const [showDetails, setShowDetails] = useState(false);
  const [selectedCard, setSelectedCard] = useState(null);

  const handleSwipe = (direction, card, index) => {
    if (direction === 'up') {
      setSelectedCard(card);
      setShowDetails(true);
      // Or navigate to a detail screen
      // navigation.navigate('CardDetails', { card });
    } else {
      console.log(`Swiped ${direction}:`, card.name);
    }
  };

  return (
    <>
      <SwipeableCardsStack
        data={cardData}
        renderCard={renderCard}
        onSwipe={handleSwipe}
      />
      
      {/* Your custom modal/overlay */}
      {showDetails && (
        <CustomModal
          card={selectedCard}
          onClose={() => setShowDetails(false)}
        />
      )}
    </>
  );
};

Advanced Callbacks

<SwipeableCardsStack
  data={cardData}
  renderCard={renderCard}
  onTap={(card, index) => {
    console.log(`Card ${index} tapped:`, card);
    // Navigate to detail view or show modal
  }}
  tapActiveOpacity={0.8}
  callbacks={{
    onSwipe: (direction, card, index) => {
      console.log(`Card ${index} swiped ${direction}`);
    },
    onSwipeStart: (card, direction) => {
      console.log(`Started swiping ${direction}`);
    },
    onSwipeEnd: (card, direction) => {
      console.log(`Ended swiping ${direction}`);
    },
    onStackEmpty: () => {
      console.log('No more cards!');
    },
    onCardFocus: (card, index) => {
      console.log(`Card ${index} is now focused`);
    },
  }}
/>

📚 API Reference

🎯 For the complete, accurate, and tested API reference, see ACTUAL_PROPS.md

This package has been cleaned up to only include props that actually work. The complete API includes:

  • Core Props: data, renderCard, keyExtractor, onTap, tapActiveOpacity
  • Styling: cardStyle, containerStyle, activeCardStyle, inactiveCardStyle, cardDimensions
  • Icons: leftSwipeIcon, rightSwipeIcon, upSwipeIcon, downSwipeIcon + styles
  • Gestures: swipeDirections, allowPartialSwipe, enableRotation, etc.
  • Callbacks: onSwipe, onSwipeStart, onSwipeEnd, onStackEmpty, onTap, etc.
  • Control: resetTrigger, currentIndex, onIndexChange
  • Configuration: thresholds, animations, stackBehavior, accessibility

Core Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | data | SwipeableCardData[] | Required | Array of card data | | renderCard | (card, index, isActive) => ReactNode | Required | Function to render each card | | keyExtractor | (card, index) => string | card.id | Extract unique key for each card | | onTap | (card, index) => void | undefined | Callback when card is tapped | | tapActiveOpacity | number | 1 | Opacity when card is pressed (0-1) |

Styling Props

| Prop | Type | Description | |------|------|-------------| | cardStyle | StyleProp<ViewStyle> | Additional styles for all cards | | activeCardStyle | StyleProp<ViewStyle> | Styles for the active (top) card | | inactiveCardStyle | StyleProp<ViewStyle> | Styles for inactive cards | | containerStyle | StyleProp<ViewStyle> | Container styles |

Swipe Icons Configuration

interface SwipeIcons {
  leftSwipeIcon?: React.ReactNode;
  rightSwipeIcon?: React.ReactNode;
  upSwipeIcon?: React.ReactNode;
  downSwipeIcon?: React.ReactNode;
  leftSwipeIconStyle?: any;
  rightSwipeIconStyle?: any;
  upSwipeIconStyle?: any;
  downSwipeIconStyle?: any;
}

Gesture Configuration

interface SwipeGestures {
  // Primary API - specify allowed directions
  swipeDirections?: Array<'left' | 'right' | 'up' | 'down'>;  // Default: all directions
  
  // Animation and visual controls
  enableRotation?: boolean;             // Enable card rotation
  enableScale?: boolean;               // Enable scale animation
  
  // Advanced gesture settings
  gestureThreshold?: number;            // Minimum movement to trigger gesture
  simultaneousGestures?: boolean;       // Allow multiple gestures
  allowPartialSwipe?: boolean;          // Allow partial swipes with spring-back
  partialSwipeReturnDuration?: number;  // Spring-back animation duration
}

Direction Control Examples

// Only horizontal swipes
<SwipeableCardsStack
  gestures={{ swipeDirections: ['left', 'right'] }}
/>

// Only vertical swipes  
<SwipeableCardsStack
  gestures={{ swipeDirections: ['up', 'down'] }}
/>

// Single direction only
<SwipeableCardsStack
  gestures={{ swipeDirections: ['right'] }}
/>

// All directions (default)
<SwipeableCardsStack
  gestures={{ swipeDirections: ['left', 'right', 'up', 'down'] }}
/>

### Animation Configuration

```tsx
interface SwipeAnimations {
  duration?: number;                    // Animation duration in ms
  rotationEnabled?: boolean;            // Enable rotation animation
  scaleEnabled?: boolean;               // Enable scale animation
  opacityEnabled?: boolean;             // Enable opacity animation
  useNativeDriver?: boolean;            // Use native driver for animations
}

Stack Behavior

interface StackBehavior {
  stackSize?: number;                   // Number of visible cards
  infiniteStack?: boolean;              // Loop cards infinitely
  stackOffset?: { x?: number; y?: number }; // Offset for stacked cards
  stackScale?: number[];                // Scale values for each card
  stackOpacity?: number[];              // Opacity values for each card
}

Callback Configuration

interface SwipeCallbacks {
  onSwipe?: (direction, card, index) => void;
  onSwipeStart?: (card, direction) => void;
  onSwipeEnd?: (card, direction) => void;
  onStackEmpty?: () => void;
  onCardFocus?: (card, index) => void;
  onAnimationComplete?: (direction, card) => void;
  onTap?: (card, index) => void;          // NEW: Card tap callback
}

🎨 Customization Examples

Dating App Style

<SwipeableCardsStack
    data={profiles}
    renderCard={renderProfile}
    callbacks={{
        onSwipe: handleSwipe,
        onSwipeStart: (profile, direction) => console.log(`Started swiping ${direction} on ${profile.name}`),
        onStackEmpty: () => console.log('No more profiles!'),
    }}
    cardDimensions={{
        width: width * 0.8,
        height: height * 0.5,
    }}
    gestures={{
        swipeDirections: ['left', 'right'], // Only allow left and right swipes
        allowPartialSwipe: true,
    }}
    rightSwipeIcon={customHeartIcon}
    leftSwipeIcon={customXIcon}
    emptyComponent={emptyComponent}
/>

🎯 Performance Tips

  1. Use keyExtractor for better list performance
  2. Memoize your renderCard function to prevent unnecessary re-renders
  3. Enable useNativeDriver for smoother animations
  4. Limit stackSize to 2-3 cards for better performance

🐛 Troubleshooting

Common Issues

Cards not responding to gestures:

  • Ensure the container has proper dimensions
  • Check direction is added in swipeDirections array
  • Check if gestures.gestureThreshold is too high

Icons not showing:

  • Verify showLeftIcon is true
  • Check if leftIconStyle is appropriate

Up swipe callback not firing:

  • Check direction is added in swipeDirections array
  • Check gestures.allowPartialSwipe is true
  • Check if thresholds.vertical or thresholds.horizontal is appropriate for your use case
  • Handle up swipe in the main onSwipe callback { ... }`

Performance issues:

  • Reduce stackSize
  • Enable animations.useNativeDriver
  • Optimize your renderCard function

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide for details.

📄 License

MIT © Westromakes

🙏 Acknowledgments

  • Inspired by Tinder's card stack interface
  • Built with React Native's powerful Animated API
  • Community feedback and contributions

⭐ Star this repo if you find it useful!

Made with ❤️ for the React Native community