React Native Swipeable Item
A swipeable component with underlay for React Native.
Fully native interactions powered by Reanimated and React Native Gesture Handler
Compatible with React Native Draggable Flatlist

Install
Props
NOTE: Naming is hard. When you swipe right, you reveal the item on the left. So what do you name these things? I have decided to name everything according to swipe direction. Therefore, a swipe left reveals the renderUnderlayLeft()
component with width underlayWidthLeft
. Not perfect but it works.
renderUnderlayLeft | (params: { item: T, percentOpen: Animated.Node<number>, open: () => void, close: () => void }) => React.ReactNode | Component to be rendered underneath row on left swipe. |
renderUnderlayRight | (params: { item: T, percentOpen: Animated.Node<number>, open: () => void, close: () => void }) => React.ReactNode | Component to be rendered underneath row on left swipe. |
underlayWidthLeft | number | Width of left-swiped underlay. |
underlayWidthRight | number | Width of left-swiped underlay. |
onChange | (params: { open: "left" | "right" | "null" }) => void | Called when row is opened or closed. |
Instance Methods
open | ("left" | "right") => void | Programmatically open left or right. |
close | () => void | Close all. |
const itemRef: SwipeRow | null = null
...
<SwipeRow ref={ref => itemRef = ref} />
...
if (itemRef) itemRef.open("left")
Example
import React from 'react';
import {
Text,
View,
StyleSheet,
FlatList,
LayoutAnimation,
TouchableOpacity,
} from 'react-native';
import Animated from 'react-native-reanimated';
import SwipeRow from 'react-native-swipeable-item';
import DraggableFlatList from 'react-native-draggable-flatlist';
const { multiply, sub } = Animated;
const NUM_ITEMS = 10;
function getColor(i) {
const multiplier = 255 / (NUM_ITEMS - 1);
const colorVal = i * multiplier;
return `rgb(${colorVal}, ${Math.abs(128 - colorVal)}, ${255 - colorVal})`;
}
const initialData = [...Array(NUM_ITEMS)].fill(0).map((d, index) => ({
text: `Row ${index}`,
key: `key-${index}`,
backgroundColor: getColor(index),
}));
class App extends React.Component {
state = {
data: initialData,
};
itemRefs = new Map();
deleteItem = (item) => {
const updatedData = this.state.data.filter(d => d !== item);
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
this.setState({ data: updatedData });
};
renderUnderlayLeft = ({ item, percentOpen }) => (
<Animated.View
style={[styles.row, styles.underlayLeft, { opacity: percentOpen }]} // Fade in on open
>
<TouchableOpacity onPressOut={() => this.deleteItem(item)}>
<Text style={styles.text}>{`[x]`}</Text>
</TouchableOpacity>
</Animated.View>
);
renderUnderlayRight = ({ item, percentOpen, close }) => (
<Animated.View
style={[styles.row, styles.underlayRight, {
transform: [{ translateX: multiply(sub(1, percentOpen), -100) }], // Translate from left on open
}]}>
<TouchableOpacity
onPressOut={close}>
<Text style={styles.text}>CLOSE</Text>
</TouchableOpacity>
</Animated.View>
);
renderItem = ({ item, index, drag }) => {
return (
<SwipeRow
key={item.key}
item={item}
ref={ref => {
if (ref && !this.itemRefs.get(item.key)) {
this.itemRefs.set(item.key, ref);
}
}}
onChange={({ open }) => {
if (open) {
// Close all other open items
[...this.itemRefs.entries()].forEach(([key, ref]) => {
if (key !== item.key && ref) ref.close();
});
}
}}
overSwipe={1000}
renderUnderlayLeft={this.renderUnderlayLeft}
underlayWidthLeft={100}
renderUnderlayRight={this.renderUnderlayRight}
underlayWidthRight={200}>
<View style={[styles.row, { backgroundColor: item.backgroundColor }]}>
<TouchableOpacity onLongPress={drag}>
<Text style={styles.text}>{item.text}</Text>
</TouchableOpacity>
</View>
</SwipeRow>
);
};
render() {
return (
<View style={styles.container}>
<DraggableFlatList
keyExtractor={item => item.key}
data={this.state.data}
renderItem={this.renderItem}
onDragEnd={({ data }) => this.setState({ data })}
/>
</View>
);
}
}
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
},
row: {
flexDirection: 'row',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 15,
},
text: {
fontWeight: 'bold',
color: 'white',
fontSize: 32,
},
underlayRight: {
flex: 1,
backgroundColor: 'teal',
justifyContent: 'flex-start',
},
underlayLeft: {
flex: 1,
backgroundColor: 'tomato',
justifyContent: 'flex-end',
},
});