High performance draggable list for Fabric using Reanimated 4
npm install react-native-reanimated-drag-listA high-performance draggable list component for React Native, built with Reanimated 4 and Gesture Handler. Runs entirely on the UI thread for buttery smooth 60fps animations.
- 🚀 UI Thread Performance - All animations run on the UI thread via Reanimated
- 📜 Progressive Auto-scroll - Automatically scrolls when dragging near edges with exponential speed curve
- ⏱️ Long Press Activation - Hold to drag, tap to scroll - configurable delay
- 🎯 Smooth Animations - Spring animations for natural feeling interactions
- 📱 Fabric Ready - Built for the new React Native architecture
- 🪆 Nestable Lists - Multiple draggable lists within a single scrollable container
- 📏 Dynamic Heights - Support for items with variable heights
- 🚫 Drag Disabled Zones - Exclude interactive elements from triggering drag
- React Native 0.71+
- react-native-reanimated 4.x
- react-native-gesture-handler 2.x
- react-native-worklets
``sh`
npm install react-native-reanimated-drag-list
Make sure you have the peer dependencies installed:
`sh`
npm install react-native-reanimated react-native-gesture-handler react-native-worklets
`tsx
import { DraggableList, type RenderItemParams } from 'react-native-reanimated-drag-list';
import { View, Text, StyleSheet } from 'react-native';
type Item = {
id: string;
title: string;
};
const data: Item[] = [
{ id: '1', title: 'Item 1' },
{ id: '2', title: 'Item 2' },
{ id: '3', title: 'Item 3' },
// ... more items
];
function App() {
const [items, setItems] = useState(data);
const renderItem = ({ item, index }: RenderItemParams
);
return (
itemHeight={60}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onDragEnd={setItems}
style={styles.list}
/>
);
}
const styles = StyleSheet.create({
list: {
flex: 1,
},
item: {
height: 60,
backgroundColor: '#fff',
justifyContent: 'center',
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
});
`
You can render multiple NestableDraggableFlatList components within a single scrollable parent using NestableScrollContainer. This is useful when you have multiple categories or sections that each need their own reorderable list.
> Note: When using NestableDraggableFlatList, React Native warnings about nested VirtualizedLists are automatically suppressed.
`tsx
import {
NestableScrollContainer,
NestableDraggableFlatList,
} from 'react-native-reanimated-drag-list';
function App() {
const [data1, setData1] = useState(initialData1);
const [data2, setData2] = useState(initialData2);
const [data3, setData3] = useState(initialData3);
const renderItem = ({ item }) => (
);
const keyExtractor = (item) => item.id;
return (
itemHeight={60}
renderItem={renderItem}
keyExtractor={keyExtractor}
onDragEnd={setData1}
/>
itemHeight={60}
renderItem={renderItem}
keyExtractor={keyExtractor}
onDragEnd={setData2}
/>
itemHeight={60}
renderItem={renderItem}
keyExtractor={keyExtractor}
onDragEnd={setData3}
/>
);
}
`
NestableDraggableFlatList supports items with variable heights. Simply omit the itemHeight prop and let items measure themselves:
`tsx
import {
NestableScrollContainer,
NestableDraggableFlatList,
} from 'react-native-reanimated-drag-list';
function App() {
const [items, setItems] = useState(data);
const renderItem = ({ item }) => (
{item.description && (
)}
);
return (
// No itemHeight - heights are measured automatically
estimatedItemHeight={80} // Optional: helps with initial layout
renderItem={renderItem}
keyExtractor={(item) => item.id}
onDragEnd={setItems}
/>
);
}
`
Use DragDisabledZone to wrap interactive elements (buttons, inputs, etc.) that should not trigger drag activation:
`tsx
import {
NestableScrollContainer,
NestableDraggableFlatList,
DragDisabledZone,
} from 'react-native-reanimated-drag-list';
function App() {
const [items, setItems] = useState(data);
const renderItem = ({ item }) => (
onPress={() => deleteItem(item.id)}
>
);
return (
itemHeight={60}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onDragEnd={setItems}
/>
);
}
`
| Prop | Type | Required | Default | Description |
| ------ | ------ | ---------- | --------- | ------------- |
| data | T[] | ✅ | - | Array of items to render |itemHeight
| | number | ✅ | - | Height of each item (must be consistent) |renderItem
| | (params: RenderItemParams | ✅ | - | Function to render each item |keyExtractor
| | (item: T) => string | ✅ | - | Function to extract unique key from item |onDragEnd
| | (data: T[]) => void | ✅ | - | Callback with reordered data after drag ends |style
| | ViewStyle | ❌ | - | Style for the ScrollView container |contentContainerStyle
| | ViewStyle | ❌ | - | Style for the content container |dragActivationDelay
| | number | ❌ | 200 | Milliseconds to hold before drag activates |
Extends all ScrollView props from react-native-gesture-handler. Supports ref forwarding to access the underlying ScrollView.
| Prop | Type | Required | Description |
| ---- | ---- | -------- | ----------- |
| children | ReactNode | ✅ | Content including NestableDraggableFlatList components |measureKey
| | number or string | ❌ | When this value changes, the container re-measures its position on screen. Useful when the container is inside an animated parent like a BottomSheet. |onScroll
| | ScrollViewProps['onScroll'] | ❌ | Scroll event handler |onScrollBeginDrag
| | ScrollViewProps['onScrollBeginDrag'] | ❌ | Called when user begins dragging the scroll view |onScrollEndDrag
| | ScrollViewProps['onScrollEndDrag'] | ❌ | Called when user stops dragging the scroll view |onMomentumScrollEnd
| | ScrollViewProps['onMomentumScrollEnd'] | ❌ | Called when momentum scroll animation ends |
#### Usage with BottomSheet
When using NestableScrollContainer inside a BottomSheet, pass the bottom sheet's index as measureKey to ensure auto-scroll works correctly when the sheet animates:
`tsx
import BottomSheet from '@gorhom/bottom-sheet';
import { NestableScrollContainer, NestableDraggableFlatList } from 'react-native-reanimated-drag-list';
function MyComponent() {
const [bottomSheetIndex, setBottomSheetIndex] = useState(1);
return (
snapPoints={['25%', '55%']}
onChange={setBottomSheetIndex}
>
itemHeight={100}
renderItem={renderItem}
onDragEnd={handleDragEnd}
keyExtractor={(item) => item.id}
/>
);
}
`
| Prop | Type | Required | Default | Description |
| ---- | ---- | -------- | ------- | ----------- |
| data | T[] | ✅ | - | Array of items to render |itemHeight
| | number | ❌ | - | Fixed height for all items. If omitted, heights are measured dynamically. |estimatedItemHeight
| | number | ❌ | 60 | Estimated item height for initial layout (only used when itemHeight is not provided) |renderItem
| | (params: RenderItemParams | ✅ | - | Function to render each item |keyExtractor
| | (item: T) => string | ✅ | - | Function to extract unique key from item |onDragEnd
| | (data: T[]) => void | ✅ | - | Callback with reordered data after drag ends |style
| | ViewStyle | ❌ | - | Style for the list container |contentContainerStyle
| | ViewStyle | ❌ | - | Style for the content container |dragActivationDelay
| | number | ❌ | 200 | Milliseconds to hold before drag activates |ListHeaderComponent
| | ReactNode | ❌ | - | Component rendered above list items |ListFooterComponent
| | ReactNode | ❌ | - | Component rendered below list items |autoScrollThreshold
| | number | ❌ | 100 | Distance from edge (in px) to trigger auto-scroll |autoScrollMaxSpeed
| | number | ❌ | 12 | Maximum auto-scroll speed (px per frame) |autoScrollMinSpeed
| | number | ❌ | 1 | Minimum auto-scroll speed (px per frame) |autoScrollSmoothing
| | number | ❌ | 0.15 | Smoothing factor for velocity transitions (0-1). Lower = smoother. |
A wrapper component that prevents drag activation on its children. Use this for buttons, inputs, or other interactive elements within draggable items.
`tsx
import { DragDisabledZone } from 'react-native-reanimated-drag-list';
`
`tsx``
type RenderItemParams
item: T; // The item data
index: number; // Current index in the list
drag: () => void; // Function to initiate drag (for custom handles)
isActive: boolean; // Whether this item is being dragged
};
1. Long press an item to activate drag mode (default 200ms)
2. Drag the item to reorder - other items animate out of the way
3. Release to drop the item in its new position
4. Scroll normally with quick swipes - dragging only activates on hold
The list automatically scrolls when you drag an item near the top or bottom edges:
- Direction-aware: Only scrolls when you're actively moving toward the edge
- Progressive speed: Uses an exponential curve - gentle near the threshold, rapid at the edge
- Smooth integration: The dragged item follows the scroll seamlessly
MIT