How to Create a Smooth, Infinite Tinder-Style Swipe Deck in React Native
Source: Dev.to
Overview
The Tinder‑style swipe card UI is an iconic and intuitive pattern for mobile apps. Whether you’re building a habit tracker, a to‑do list, or a social app, giving users the ability to “swipe” through items is incredibly engaging.
In this guide we’ll show how to create an infinite, smooth swipe deck in React Native using:
rn‑swiper‑list– core library for the swipe deck- React Query – for fetching data (e.g., a list of habits)
- Zustand – simple state management
Creating an Infinite Deck
When a user swipes the last card we want the first card to reappear. The trick is to never remove the swiped card from the data array. Instead, move the first item to the end of the array.
// Example state (Zustand or useState)
const [habits, setHabits] = useState(initialHabits);
const moveToEnd = () => {
setHabits(prev => {
const [first, ...rest] = prev;
return [...rest, first];
});
};
By passing a key prop to the <Swiper> component that depends on the data (e.g., key={habits.map(h => h.id).join('-')}), the state update forces React to re‑render the deck, effectively looping the cards.
Preventing Lag and Janky Animations
Why Lag Happens
rn‑swiper‑list defaults to a swipe animation of about 500 ms. If the state is updated immediately when the swipe occurs, the component re‑renders before the animation finishes, causing a noticeable lag.
The setTimeout Trick
Delay the state update until the animation completes:
const onSwipe = () => {
// Do NOT update state here – it causes lag
// moveToEnd(); // ❌
// Update after the animation duration (500 ms)
setTimeout(() => {
moveToEnd(); // ✅
}, 500);
};
return (
<Swiper
onSwipedLeft={onSwipe}
onSwipedRight={onSwipe}
// other props…
/>
);
The 500 ms delay matches the default animation duration, allowing the card to swipe off‑screen completely before the data array is reordered. The next card then slides in smoothly, giving a seamless infinite loop.
Adding Custom Swipe Buttons Inside Cards
If you prefer internal “like” or “nope” buttons instead of external controls, you can trigger swipes programmatically via a ref.
Step‑by‑Step
-
Create a ref in the parent component:
const swiperRef = useRef(null); -
Pass the ref to the
<Swiper>component:<Swiper ref={swiperRef} /* other props */ /> -
Forward the ref to each card component (if needed).
-
Use the ref inside
HabitCardto call built‑in swipe methods:// Inside HabitCard.tsx import { Pressable, Text } from 'react-native'; const HabitCard = ({ card, swiperRef }) => ( <> {/* Render card content */} <Pressable onPress={() => swiperRef.current?.swipeLeft()}> <Text>Nope</Text> </Pressable> <Pressable onPress={() => swiperRef.current?.swipeRight()}> <Text>Like</Text> </Pressable> </> );
These buttons give you full control over the swipe deck, enabling complex, custom UI interactions similar to Tinder or Bumble.
Summary
- Keep the data array intact and rotate items to achieve an infinite loop.
- Delay state updates with
setTimeout(matching the animation duration) to avoid lag. - Use a ref to trigger swipes from inside card components for custom button controls.
With these techniques, you can build a performant, infinite Tinder‑style swipe deck in React Native.