Overview of React Native Sortables
react-native-reanimated and react-native-gesture-handler.website·Indexed 29 days ago
https://react-native-sortables-docs.vercel.appA library for creating sortable layouts in React Native, featuring Sortable Grid and Sortable Flex components. It includes support for custom handles, drop indicators, active item portals, and layout animations, with integration for Reanimated Shared Values.
react-native-reanimated and react-native-gesture-handler.react-native-reanimated. Performance is smooth on the Old Architecture and in production release builds.Haptic feedback is optional. The library automatically detects and uses the first available haptics package it finds in the following priority order:
react-native-pulsar: Provides rich haptic presets; requires the New Architecture.expo-haptics: Recommended for Expo applications.react-native-haptic-feedback: Recommended for bare React Native workflows.'worklet' directive at the start of the function body. Plain JS functions are supported but run on the JS thread via runOnJS and are less performant.const onDragStart = useCallback((params: DragStartParams) => {
'worklet';
// This runs on the UI thread
}, []);Sortable.Flex component, use the showDropIndicator prop.import Sortable from 'react-native-sortables';
<Sortable.Flex
gap={10}
showDropIndicator
>
{/* items */}
</Sortable.Flex>Sortable.Touchable component is used to add interactive elements (like buttons or links) inside sortable items. It is specifically designed to handle touch gestures (tap, double tap, long press) without conflicting with the drag-and-drop gesture detector used for item activation and movement.import Sortable from 'react-native-sortables';
<Sortable.Touchable
onTap={() => {
console.log('tapped');
}}
onLongPress={() => {
console.log('long pressed');
}}
onTouchesDown={() => {
console.log('touch started');
}}
>
{/* Your interactive content here */}
</Sortable.Touchable>useItemContext hook provides access to item-related values and drag states. It must be called within a component that is rendered as part of a sortable item. The hook returns an object containing the item's key, its active status, animation progress, and the gesture object required for custom drag handles.import { useItemContext } from 'react-native-sortables';
const ctx = useItemContext(); // Must be used inside a sortable item componentThe Sortable.Layer component automatically manages zIndex values to ensure that items being dragged in a sortable list or grid remain visible above other components on the screen. It detects when a child item starts being dragged and adjusts the visual hierarchy accordingly. If multiple Sortable.Layer components are nested, the drag information bubbles up to update the zIndex in each layer.
Most sortable components use Sortable.Layer internally, so it should only be used explicitly if you notice dragged items appearing underneath other UI elements.
import Sortable from 'react-native-sortables';
<Sortable.Layer>
{/* Other components */}
<Sortable.Grid
// sortable grid props
/>
{/* Other components */}
</Sortable.Layer>DropIndicatorComponent prop. The custom component receives DropIndicatorComponentProps, which includes animation progress, the active item key, the current drop index, and the ordered list of item keys.import type { DropIndicatorComponentProps } from 'react-native-sortables';
import Animated from 'react-native-reanimated';
function CustomDropIndicator({
activeAnimationProgress,
activeItemKey,
dropIndex,
orderedItemKeys,
style
}: DropIndicatorComponentProps) {
// Use Reanimated hooks (useDerivedValue, useAnimatedStyle, etc.)
// to create custom animations based on the props
return (
<Animated.View style={[style, { backgroundColor: 'red' }]} />
);
}
export default function App() {
return (
<Sortable.Grid
showDropIndicator
DropIndicatorComponent={CustomDropIndicator}
{...otherProps}
/>
);
}Sortable.Touchable component when you need to detect press events (like button clicks) on elements nested inside a sortable item. This is specifically useful for components located within the area that detects drag gestures (either the entire item or a Sortable.Handle). Using Sortable.Touchable ensures that a press event is triggered only when the user intends to tap, and not when the item is being dragged.import { useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Sortable from 'react-native-sortables';
const INITIAL_DATA = ['Poland', 'Germany', 'France'];
export default function FlexTouchableExample() {
const [data, setData] = useState(INITIAL_DATA);
return (
<View style={styles.container}>
<Sortable.Flex
gap={10}
onDragEnd={({ order }) => setData(order(data))}>
{data.map(item => (
<View key={item} style={styles.card}>
<Text style={styles.text}>{item}</Text>
{/* Use Sortable.Touchable for buttons inside the draggable area */}
<Sortable.Touchable
style={styles.deleteButton}
onTap={() => setData(prev => prev.filter(i => i !== item))}>
<Text style={styles.text}>✕</Text>
</Sortable.Touchable>
</View>
))}
</Sortable.Flex>
</View>
);
}
const styles = StyleSheet.create({
card: {
flexDirection: 'row',
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: 'blue',
borderRadius: 9999,
gap: 10,
alignItems: 'center'
},
deleteButton: {
width: 22,
height: 22,
borderRadius: 9999,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
justifyContent: 'center',
alignItems: 'center'
},
container: { flex: 1, padding: 10 },
text: { color: 'white', fontWeight: 'bold' }
});