Skip to content

Tinder Cards

An easy to integrate onboarding screens with smooth color interpolation. Just pass Images, Texts and colors and it’s ready!!

Alt text

Installation

  1. Install React Native Reanimated

    Terminal window
    npm install react-native-reanimated
  2. Add Reanimated plugin into babel.config file

    module.exports = {
    presets: [
    ... // don't add it here :)
    ],
    plugins: [
    ...
    'react-native-reanimated/plugin',
    ],
    };
  3. Install react-native-gesture-handler

    Terminal window
    npm install react-native-gesture-handler
  4. Wrap your app inside <GestureHandlerRootView> (make sure it’s at the root of your project)

    export default function App() {
    return (
    <GestureHandlerRootView>
    {/*components go here*/}
    </GestureHandlerRootView>
    );
    }
  5. Copy core component src/components/tinderCards.tsx

    import React, { useState } from 'react';
    import { View, Text, StyleSheet, Dimensions, ImageBackground } from 'react-native';
    import Animated, {
    useSharedValue,
    useAnimatedStyle,
    withSpring,
    runOnJS,
    } from 'react-native-reanimated';
    import {
    GestureDetector,
    Gesture,
    } from 'react-native-gesture-handler';
    import LinearGradient from 'react-native-linear-gradient';
    const { width, height } = Dimensions.get('window');
    const SWIPE_THRESHOLD = width * 0.25;
    interface Card {
    id: number;
    name: string;
    age:number;
    description: string;
    image: string;
    }
    interface TinderCardProps {
    card: Card;
    onSwipeLeft: (card: Card) => void
    onSwipeRight: (card: Card) => void;
    }
    const TinderCard: React.FC<TinderCardProps> = ({ card, onSwipeLeft, onSwipeRight }) => {
    const translateX = useSharedValue(0);
    const translateY = useSharedValue(0);
    const rotate = useSharedValue(0);
    // Gesture for pan handling
    const panGesture = Gesture.Pan()
    .onUpdate((event) => {
    translateX.value = event.translationX;
    translateY.value = event.translationY;
    rotate.value = translateX.value / width;
    })
    .onEnd(() => {
    if (translateX.value > SWIPE_THRESHOLD) {
    runOnJS(onSwipeRight)(card);
    translateX.value = withSpring(width);
    } else if (translateX.value < -SWIPE_THRESHOLD) {
    runOnJS(onSwipeLeft)(card);
    translateX.value = withSpring(-width);
    } else {
    translateX.value = withSpring(0);
    translateY.value = withSpring(0);
    rotate.value = withSpring(0);
    }
    });
    // Styles for the animated card
    const animatedStyle = useAnimatedStyle(() => {
    return {
    transform: [
    { translateX: translateX.value },
    { translateY: translateY.value },
    { rotate: `${rotate.value * 15}deg` },
    ],
    };
    });
    return (
    <GestureDetector gesture={panGesture}>
    <Animated.View style={[styles.card, animatedStyle]}>
    <ImageBackground source={{ uri: card.image }} style={styles.image} >
    <View style={styles.cardContent}>
    <LinearGradient
    colors={['transparent', 'rgba(0,0,0,0.7)']}
    style={styles.gradientOverlay}
    /><View style={{flexDirection:'column'}}>
    <Text style={styles.name}>{card.name}, {card.age}</Text>
    <Text style={styles.description}>{card.description}</Text>
    </View>
    <Text style={styles.description}> info </Text>
    </View>
    </ImageBackground>
    </Animated.View>
    </GestureDetector>
    );
    };
    type profile = {
    id:number,
    name:string,
    age:number,
    description:string,
    image:string
    }
    interface TinderCardsProps {
    profiles : profile[],
    }
    const TinderCards: React.FC <TinderCardsProps> = ({profiles}) => {
    const [cards, setCards] = useState<Card[]>(profiles);
    const handleSwipeLeft = (card: Card) => {
    console.log('Swiped Left:', card.name);
    setCards((prevCards) => prevCards.filter((c) => c.id !== card.id));
    };
    const handleSwipeRight = (card: Card) => {
    console.log('Swiped Right:', card.name);
    setCards((prevCards) => prevCards.filter((c) => c.id !== card.id));
    };
    return (
    <View style={styles.container}>
    {cards
    .map((card, index) => (
    <TinderCard
    key={card.id}
    card={card}
    onSwipeLeft={handleSwipeLeft}
    onSwipeRight={handleSwipeRight}
    />
    ))
    .reverse()}
    </View>
    );
    };
    export default TinderCards;
    const styles = StyleSheet.create({
    container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    },
    card: {
    width: width * 0.9,
    height: height * 0.65,
    borderRadius: 10,
    backgroundColor: 'white',
    position: 'absolute',
    justifyContent: 'flex-start',
    overflow: 'hidden',
    elevation: 1,
    },
    image: {
    width: '100%',
    height: '100%',
    borderTopLeftRadius: 10,
    borderTopRightRadius: 10,
    },
    cardContent: {
    padding: 20,
    flex:1,
    flexDirection:'row',
    justifyContent:'space-between',
    alignItems:'flex-end',
    },
    name: {
    fontSize: 24,
    fontWeight: 'bold',
    color:'white'
    },
    description: {
    fontSize: 16,
    color: 'white',
    },
    gradientOverlay: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    height: '30%', // Adjust the height as needed
    },
    });

Props

PropTypeDescription
profilesprofile[]Array of objects where each objects have 5 properties(id, name, age, description, image).

Important

To handle what happens if user swipe left or right, you have to do that inside the component (handleSwipeLeft and handleSwipeRight)