Tinder Cards
An easy to integrate onboarding screens with smooth color interpolation. Just pass Images, Texts and colors and it’s ready!!
import { StyleSheet } from 'react-native'import React from 'react'import TinderCards from '../src/components/tinderCards'import { GestureHandlerRootView } from 'react-native-gesture-handler';
const cards = [ { id: 1, name: 'John Doe', age: 20 , description: 'Lorem ipsum', image: 'https://images.ctfassets.net/h6goo9gw1hh6/2sNZtFAWOdP1lmQ33VwRN3/24e953b920a9cd0ff2e1d587742a2472/1-intro-photo-final.jpg?w=1200&h=992&fl=progressive&q=70&fm=jpg' }, { id: 2, name: 'Jane Smith', age: 24 , description: 'Lorem ipsum', image: 'https://writestylesonline.com/wp-content/uploads/2018/11/Three-Statistics-That-Will-Make-You-Rethink-Your-Professional-Profile-Picture-1024x1024.jpg' }, { id: 3, name: 'Dwight', age: 35 , description: 'Bears, Beats, BattleStar Galactica', image: 'https://akns-images.eonline.com/eol_images/Entire_Site/2013221/TheOfficePranks6.jpg?fit=around%7C776:576&output-quality=90&crop=776:576;center,top' }, { id: 4, name: 'Anurag Negi', age: 21 , description: 'Is this grindr??', image: 'https://via.placeholder.com/300' },]const App = () => {
return ( <GestureHandlerRootView> <TinderCards profiles={cards} /> </GestureHandlerRootView> )}
export default AppInstallation
-
Install React Native Reanimated
Terminal window npm install react-native-reanimatedTerminal window yarn add react-native-reanimatedTerminal window npx expo install react-native-reanimated -
Add Reanimated plugin into babel.config file
module.exports = {presets: [... // don't add it here :)],plugins: [...'react-native-reanimated/plugin',],}; -
Install
react-native-gesture-handlerTerminal window npm install react-native-gesture-handlerTerminal window yarn add react-native-gesture-handlerTerminal window npx expo install react-native-gesture-handler -
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>);} -
Copy core component
src/components/tinderCards.tsximport 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) => voidonSwipeRight: (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 handlingconst 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 cardconst 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}><LinearGradientcolors={['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) => (<TinderCardkey={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
| Prop | Type | Description |
|---|---|---|
profiles | profile[] | 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)