A drag-and-drop library that finally works on React Native ✨
Powerful, performant, and built for the modern React Native developer
After countless attempts with drag-and-drop solutions that don't work or are simply outdated, this is something that finally works. And it is not just another DnD library, but a complete ecosystem built from the ground up for React Native, offering a best-in-class developer experience and production-ready performance.
Highly feature-packed with every interaction pattern you'll ever need, yet simple enough to get started in minutes. Built for developers who demand both power and simplicity.
- 🚀 High Performance - Built with Reanimated 3 for buttery-smooth 60fps animations
- 🏗️ Full RN Fabric Support - Works seamlessly with both New Architecture and Old Architecture
- 📦 Expo Compatible - Zero configuration needed, works out of the box with Expo
- 🪶 Tiny Bundle Size - Only 70kb unpacked size, won't bloat your app
- 🎯 Flexible API - From simple drag-and-drop to complex sortable lists
- 📱 React Native First - Designed specifically for mobile, not ported from web
- 🔧 TypeScript Ready - Full type safety with comprehensive definitions
- 🎨 Infinitely Customizable - Every animation, behavior, and style is configurable
- 📦 Complete Component Suite - Draggable, Droppable, Sortable, and more
- 🎪 Smart Collision Detection - Multiple algorithms (center, intersect, contain)
- 📜 Sortable Lists - Drag and drop to sort a Vertical List, also supports Automatic scrolling for out of screen dragging
- 🎭 Drag Handles - Precise control with dedicated drag areas
- 🎬 Custom Animations - Spring, timing, or bring your own animation functions
- 📐 Pixel-Perfect Positioning - 9-point alignment system with custom offsets
- 📦 Boundary Constraints - Keep draggables within specific areas
- ⚡ State Management - Complete lifecycle tracking and callbacks
- 🎯 Developer Experience - Intuitive APIs, helpful warnings, and extensive examples
See it in action! A comprehensive example app with 15 interactive demos showcasing every feature and use case.
📱 Scan & Play Scan with your camera or Expo Go app |
🚀 Quick Start
Or browse the code: 📂 View Example App → |
Comprehensive guides, API reference, and interactive examples
The example app includes:
- 🎵 Sortable Music Queue - Complete list reordering with handles
- 🎯 Collision Detection - Different algorithms in action
- 🎬 Custom Animations - Spring, timing, and easing variations
- 📦 Boundary Constraints - Axis-locked and bounded dragging
- ✨ Visual Feedback - Active styles and state management
- ⚙️ Advanced Patterns - Custom implementations and hooks
See the library in action with these demos showcasing some of the key features and use cases.
Drag and drop to reorder items with smooth animations https://github.com/user-attachments/assets/1cd1929c-724b-4dda-a916-f3e69f917f7b Features: Auto-scrolling • Drag handles • Smooth transitions |
Multiple algorithms for precise drop targeting https://github.com/user-attachments/assets/379040d7-8489-430b-bae4-3fcbde34264e Algorithms: Center • Intersect • Contain |
Precise control with dedicated drag areas https://github.com/user-attachments/assets/ec051d5b-8ba0-41b7-86ae-379de26a97dd Features: Touch-friendly • Visual feedback • Accessibility |
Constrain movement within specific boundaries https://github.com/user-attachments/assets/7bd5045b-47c4-4d9b-a0c5-eb89122ec9c0 Constraints: Axis-locked • Container bounds • Custom limits |
Visual feedback during drag operations https://github.com/user-attachments/assets/3b8a3d00-38ad-4532-bd42-173037ea61b9 Feedback: Hover states • Drop zones • Visual cues |
Complete lifecycle tracking and callbacks https://github.com/user-attachments/assets/da5e526f-f2d2-4dc5-96b5-3fecc4faf57a States: Idle • Dragging • Animating • Dropped |
npm install react-native-reanimated-dnd
npm install react-native-reanimated react-native-gesture-handler
Follow the setup guides:
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { Draggable, DropProvider } from "react-native-reanimated-dnd";
export default function App() {
return (
<GestureHandlerRootView style={styles.container}>
<DropProvider>
<View style={styles.content}>
<Draggable data={{ id: "1", title: "Drag me!" }}>
<View style={styles.draggableItem}>
<Text style={styles.itemText}>🎯 Drag me around!</Text>
</View>
</Draggable>
</View>
</DropProvider>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#000000",
},
content: {
flex: 1,
padding: 20,
justifyContent: "center",
alignItems: "center",
},
draggableItem: {
padding: 20,
backgroundColor: "#1C1C1E",
borderRadius: 12,
borderWidth: 1,
borderColor: "#3A3A3C",
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 3,
},
itemText: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "600",
textAlign: "center",
},
});
import React from "react";
import { Alert, StyleSheet, Text, View } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import {
Draggable,
Droppable,
DropProvider,
} from "react-native-reanimated-dnd";
export default function DragDropExample() {
const handleDrop = (data: any, zoneId: string) => {
Alert.alert("Item Dropped", `"${data.title}" dropped in ${zoneId}`);
};
return (
<GestureHandlerRootView style={styles.container}>
<DropProvider>
<View style={styles.content}>
{/* Drop Zones */}
<View style={styles.dropZonesSection}>
<Text style={styles.sectionTitle}>Drop Zones</Text>
<Droppable
onDrop={(data) => handleDrop(data, "Zone 1")}
activeStyle={styles.dropZoneActive}
style={styles.droppable}
>
<View style={[styles.dropZoneBlue, styles.dropZone]}>
<Text style={styles.dropZoneText}>🎯 Zone 1</Text>
<Text style={styles.dropZoneSubtext}>Drop here</Text>
</View>
</Droppable>
<Droppable
onDrop={(data) => handleDrop(data, "Zone 2")}
activeStyle={styles.dropZoneActive}
style={styles.droppable}
>
<View style={[styles.dropZone, styles.dropZoneGreen]}>
<Text style={styles.dropZoneText}>🎯 Zone 2</Text>
<Text style={styles.dropZoneSubtext}>Drop here</Text>
</View>
</Droppable>
</View>
{/* Draggable Item */}
<View style={styles.draggableSection}>
<Text style={styles.sectionTitle}>Draggable Item</Text>
<Draggable data={{ id: "1", title: "Task Item" }}>
<View style={styles.draggableItem}>
<Text style={styles.itemText}>📦 Drag me to a zone</Text>
</View>
</Draggable>
</View>
</View>
</DropProvider>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#000000",
},
content: {
flex: 1,
padding: 20,
justifyContent: "space-between",
},
sectionTitle: {
color: "#FFFFFF",
fontSize: 18,
fontWeight: "700",
marginBottom: 20,
textAlign: "center",
},
draggableSection: {
alignItems: "center",
paddingVertical: 40,
},
draggableItem: {
padding: 20,
backgroundColor: "#1C1C1E",
borderRadius: 12,
borderWidth: 1,
borderColor: "#3A3A3C",
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 3,
},
itemText: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "600",
textAlign: "center",
},
dropZonesSection: {
flex: 1,
paddingVertical: 40,
},
droppable: {
marginBottom: 20,
overflow: "hidden",
borderRadius: 16,
},
dropZone: {
height: 140,
borderWidth: 2,
borderStyle: "dashed",
borderRadius: 16,
justifyContent: "center",
alignItems: "center",
padding: 20,
},
dropZoneBlue: {
borderColor: "#58a6ff",
backgroundColor: "rgba(88, 166, 255, 0.08)",
},
dropZoneGreen: {
borderColor: "#3fb950",
backgroundColor: "rgba(63, 185, 80, 0.08)",
},
dropZoneActive: {
backgroundColor: "rgba(255, 255, 255, 0.1)",
borderStyle: "solid",
transform: [{ scale: 1.02 }],
},
dropZoneText: {
color: "#FFFFFF",
fontSize: 18,
fontWeight: "600",
textAlign: "center",
marginBottom: 8,
},
dropZoneSubtext: {
color: "#8E8E93",
fontSize: 14,
textAlign: "center",
},
});
import React, { useCallback, useState } from "react";
import { StyleSheet, Text, View } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import {
Sortable,
SortableItem,
SortableRenderItemProps,
} from "react-native-reanimated-dnd";
interface Task {
id: string;
title: string;
completed: boolean;
}
export default function SortableExample() {
const [tasks, setTasks] = useState<Task[]>([
{ id: "1", title: "Learn React Native", completed: false },
{ id: "2", title: "Build an app", completed: false },
{ id: "3", title: "Deploy to store", completed: true },
{ id: "4", title: "Celebrate success", completed: false },
]);
const renderTask = useCallback(
(props: SortableRenderItemProps<Task>) => {
const {
item,
id,
positions,
lowerBound,
autoScrollDirection,
itemsCount,
itemHeight,
} = props;
return (
<SortableItem
key={id}
data={item}
id={id}
positions={positions}
lowerBound={lowerBound}
autoScrollDirection={autoScrollDirection}
itemsCount={itemsCount}
itemHeight={itemHeight}
onMove={(itemId, from, to) => {
const newTasks = [...tasks];
const [movedTask] = newTasks.splice(from, 1);
newTasks.splice(to, 0, movedTask);
setTasks(newTasks);
}}
style={styles.taskItem}
>
<View style={styles.taskContent}>
<View style={styles.taskInfo}>
<Text style={styles.taskTitle}>{item.title}</Text>
<Text style={styles.taskStatus}>
{item.completed ? "✅ Completed" : "⏳ Pending"}
</Text>
</View>
{/* Drag Handle */}
<SortableItem.Handle style={styles.dragHandle}>
<View style={styles.dragIconContainer}>
<View style={styles.dragColumn}>
<View style={styles.dragDot} />
<View style={styles.dragDot} />
<View style={styles.dragDot} />
</View>
<View style={styles.dragColumn}>
<View style={styles.dragDot} />
<View style={styles.dragDot} />
<View style={styles.dragDot} />
</View>
</View>
</SortableItem.Handle>
</View>
</SortableItem>
);
},
[tasks]
);
return (
<GestureHandlerRootView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>📋 My Tasks</Text>
<Text style={styles.headerSubtitle}>Drag to reorder</Text>
</View>
<Sortable
data={tasks}
renderItem={renderTask}
itemHeight={80}
style={styles.list}
/>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#000000",
},
header: {
padding: 20,
paddingBottom: 16,
borderBottomWidth: 1,
borderBottomColor: "#2C2C2E",
},
headerTitle: {
color: "#FFFFFF",
fontSize: 24,
fontWeight: "700",
marginBottom: 4,
},
headerSubtitle: {
color: "#8E8E93",
fontSize: 14,
},
list: {
flex: 1,
backgroundColor: "#000000",
marginTop: 20,
paddingHorizontal: 20,
borderRadius: 20,
overflow: "hidden",
},
taskItem: {
height: 80,
backgroundColor: "transparent",
},
taskContent: {
flex: 1,
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 20,
backgroundColor: "#1C1C1E",
borderWidth: 1,
borderColor: "#3A3A3C",
},
taskInfo: {
flex: 1,
paddingRight: 16,
},
taskTitle: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "600",
marginBottom: 4,
},
taskStatus: {
color: "#8E8E93",
fontSize: 14,
},
dragHandle: {
padding: 12,
borderRadius: 8,
backgroundColor: "rgba(255, 255, 255, 0.05)",
},
dragIconContainer: {
flexDirection: "row",
alignItems: "center",
gap: 3,
},
dragColumn: {
flexDirection: "column",
gap: 2,
},
dragDot: {
width: 3,
height: 3,
borderRadius: 1.5,
backgroundColor: "#6D6D70",
},
});
Makes any component draggable with extensive customization options.
<Draggable
data={any} // Data associated with the item
onDragStart={(data) => void} // Called when dragging starts
onDragEnd={(data) => void} // Called when dragging ends
onDragging={(position) => void} // Called during dragging
onStateChange={(state) => void} // Called on state changes
dragDisabled={boolean} // Disable dragging
collisionAlgorithm="center|intersect|contain" // Collision detection method
dragAxis="x|y|both" // Constrain movement axis
dragBoundsRef={RefObject} // Boundary container reference
animationFunction={(toValue) => Animation} // Custom animation function
style={StyleProp<ViewStyle>} // Component styling
>
{children}
</Draggable>
Creates drop zones with visual feedback and capacity management.
<Droppable
onDrop={(data) => void} // Called when item is dropped
onActiveChange={(isActive) => void} // Called on hover state change
dropDisabled={boolean} // Disable drop functionality
dropAlignment="top-left|center|bottom-right|..." // Drop positioning
dropOffset={{ x: number, y: number }} // Position offset
activeStyle={StyleProp<ViewStyle>} // Style when active
capacity={number} // Maximum items allowed
droppableId={string} // Unique identifier
>
{children}
</Droppable>
High-level component for sortable lists with auto-scrolling.
<Sortable
data={Array<{ id: string }>} // Array of items to render
renderItem={(props) => ReactNode} // Render function for items
itemHeight={number} // Height of each item
itemKeyExtractor={(item) => string} // Custom key extractor
style={StyleProp<ViewStyle>} // List container style
contentContainerStyle={StyleProp<ViewStyle>} // Content container style
/>
Individual item within a sortable list with gesture handling.
<SortableItem
id={string} // Unique identifier
data={any} // Item data
positions={SharedValue} // Position tracking
onMove={(id, from, to) => void} // Called when item moves
onDragStart={(id, position) => void} // Called when dragging starts
onDrop={(id, position) => void} // Called when item is dropped
onDragging={(id, overItemId, y) => void} // Called during dragging
style={StyleProp<ViewStyle>} // Item styling
animatedStyle={StyleProp<AnimatedStyle>} // Animated styling
>
{children}
</SortableItem>
Core hook for implementing draggable functionality.
Core hook for implementing droppable functionality.
Hook for individual sortable items with position management.
Hook for managing entire sortable lists with auto-scrolling.
Required context provider that manages global drag-and-drop state.
<DropProvider>{/* All draggable and droppable components */}</DropProvider>
enum DraggableState {
IDLE = "idle",
DRAGGING = "dragging",
ANIMATING = "animating",
}
type CollisionAlgorithm = "center" | "intersect" | "contain";
type DropAlignment =
| "top-left"
| "top-center"
| "top-right"
| "center-left"
| "center"
| "center-right"
| "bottom-left"
| "bottom-center"
| "bottom-right";
import { withTiming, withSpring, Easing } from "react-native-reanimated";
// Smooth timing animation
const smoothAnimation = (toValue) => {
"worklet";
return withTiming(toValue, {
duration: 300,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
});
};
// Spring animation
const springAnimation = (toValue) => {
"worklet";
return withSpring(toValue, {
damping: 15,
stiffness: 150,
});
};
<Draggable animationFunction={springAnimation}>{/* content */}</Draggable>;
// Precise center-point collision
<Draggable collisionAlgorithm="center">
{/* Requires center point to be over drop zone */}
</Draggable>
// Forgiving intersection collision (default)
<Draggable collisionAlgorithm="intersect">
{/* Any overlap triggers collision */}
</Draggable>
// Strict containment collision
<Draggable collisionAlgorithm="contain">
{/* Entire draggable must be within drop zone */}
</Draggable>
<SortableItem id={item.id} {...props}>
<View style={styles.itemContainer}>
<Text>{item.title}</Text>
{/* Only this handle area can initiate dragging */}
<SortableItem.Handle style={styles.dragHandle}>
<View style={styles.handleIcon}>
<View style={styles.dot} />
<View style={styles.dot} />
<View style={styles.dot} />
</View>
</SortableItem.Handle>
</View>
</SortableItem>
const containerRef = useRef<View>(null);
<View ref={containerRef} style={styles.container}>
<Draggable
data={data}
dragBoundsRef={containerRef}
dragAxis="x" // Constrain to horizontal movement
>
{/* content */}
</Draggable>
</View>;
<Droppable
capacity={3}
onDrop={(data) => {
if (currentItems.length < 3) {
addItem(data);
}
}}
activeStyle={{
backgroundColor: currentItems.length < 3 ? "#e8f5e8" : "#ffe8e8",
}}
>
<Text>Drop Zone ({currentItems.length}/3)</Text>
</Droppable>
- Clone the repository:
git clone https://github.com/entropyconquers/react-native-reanimated-dnd.git
cd react-native-reanimated-dnd
- Install dependencies:
npm install
cd example-app
npm install
- Run the example app:
# iOS
npx expo run:ios
# Android
npx expo run:android
The example app includes all 15 interactive examples showcasing every feature of the library.
I am constantly working to improve React Native Reanimated DnD. Here's what's coming next:
Focus: Enhanced Functionality & Bug Fixes
-
🐛 Bug Fixes & Issues Resolution
- Address existing reported issues
- Performance optimizations
- Gesture handling improvements
- API Improvements
-
📐 Sortable Grids
- 2D grid drag-and-drop support
- Flexible grid layouts (2x2, 3x3, custom)
- Smart auto-positioning and gap management
- Responsive grid behavior
-
↔️ Horizontal Sortable Lists- Full horizontal scrolling support
- Auto-scroll for out-of-view items
- Customizable scroll behavior
-
🪆 Nested Sortable Lists
- Multi-level hierarchy support
- Collapse/expand functionality
- Parent-child relationship management
- Tree-like data structure handling
-
📋 Kanban Board Support
- Cross-list dragging capabilities
- Multiple column support
- Inter-list item transfer
- Board-level state management
Vote on features you'd like to see by raising an issue.
Have an idea? Open a feature request and let me know!
Contributions are always welcome! We believe in building this library together with the community.
Ways to contribute:
- 🐛 Report bugs and issues
- ✨ Suggest new features
- 🔧 Submit pull requests
- 📚 Improve documentation
- 🧪 Write tests
- 💬 Help others in discussions
Please see our Contributing Guide for detailed information on:
- Setting up the development environment
- Code style guidelines
- Pull request process
- Testing requirements
- Community guidelines
MIT © Vishesh Raheja
- Built with React Native Reanimated for smooth 60fps animations
- Gesture handling powered by React Native Gesture Handler
- Inspired by the React ecosystem's drag-and-drop libraries
- Special thanks to the React Native community for feedback and contributions