Basic Concepts
Understanding the core concepts and architecture of React Native Reanimated DnD.
Architecture Overview
The library is built around a context-based architecture that enables seamless communication between draggable and droppable components.
Core Components
App
├── GestureHandlerRootView (Required)
└── DropProvider (Context)
├── Draggable (Items that can be moved)
├── Droppable (Areas where items can be dropped)
└── Sortable (Special case for reorderable lists)
└── SortableItem (Individual items in sortable lists)
Component Hierarchy
- GestureHandlerRootView: Required wrapper for gesture handling
- DropProvider: Creates the drag-and-drop context
- Draggable/Droppable: Core interaction components
- Sortable/SortableItem: Higher-level components for lists
Core Concepts
1. Context-Based Communication
All drag-and-drop functionality relies on React Context for component communication:
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { DropProvider } from 'react-native-reanimated-dnd';
// The DropProvider creates a context that enables:
// - Registration of draggable and droppable components
// - Collision detection between components
// - State management for active drags
// - Position tracking and updates
function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<DropProvider>
{/* All drag-and-drop components must be children of DropProvider */}
<YourComponents />
</DropProvider>
</GestureHandlerRootView>
);
}
2. Data-Driven Interactions
Every draggable component carries data that flows through the drag-and-drop system:
// Data flows from draggable to droppable
<Draggable data={{ id: '1', name: 'Task 1', priority: 'high' }}>
<Text>Drag me!</Text>
</Draggable>
<Droppable onDrop={(data) => {
// Receives: { id: '1', name: 'Task 1', priority: 'high' }
console.log('Received:', data);
}}>
<Text>Drop zone</Text>
</Droppable>
3. Collision Detection
The library automatically handles collision detection between draggable items and drop zones:
// Different collision algorithms available:
<Draggable
data={data}
collisionAlgorithm="intersect" // Default - any overlap
>
<Text>Easy to drop</Text>
</Draggable>
<Draggable
data={data}
collisionAlgorithm="center" // Center point must be over droppable
>
<Text>Precise dropping</Text>
</Draggable>
<Draggable
data={data}
collisionAlgorithm="contain" // Entire item must be contained
>
<Text>Strict containment</Text>
</Draggable>
4. Animation System
Built on React Native Reanimated for smooth, performant animations:
// Automatic spring animations on drop
<Draggable
data={data}
animationFunction={(toValue) => {
'worklet';
return withSpring(toValue, {
damping: 15,
stiffness: 150
});
}}
>
<Text>Custom animation</Text>
</Draggable>
Component Types
Draggable Components
Make any React Native component draggable:
import { Draggable } from 'react-native-reanimated-dnd';
function TaskItem({ task }) {
return (
<Draggable data={task}>
<View style={styles.taskCard}>
<Text>{task.title}</Text>
<Text>Priority: {task.priority}</Text>
</View>
</Draggable>
);
}
Key Features:
- Automatic gesture handling
- Collision detection
- Animation support
- Boundary constraints
- Axis restrictions
- Handle-based dragging
Droppable Components
Create areas where draggable items can be dropped:
import { Droppable } from 'react-native-reanimated-dnd';
function DropZone({ onTaskDrop }) {
return (
<Droppable
onDrop={onTaskDrop}
dropAlignment="center"
capacity={5}
>
<View style={styles.dropZone}>
<Text>Drop tasks here</Text>
</View>
</Droppable>
);
}
Key Features:
- Drop event handling
- Visual feedback for active drops
- Alignment options for dropped items
- Capacity limits
- Custom styling for different states
Sortable Components
High-level components for reorderable lists:
import { Sortable, SortableItem } from 'react-native-reanimated-dnd';
import { useCallback } from 'react';
function TaskList({ tasks, onTaskReorder }) {
const renderTask = useCallback(({ item, id, positions, ...props }) => (
<SortableItem
key={id}
id={id}
positions={positions}
{...props}
onMove={(itemId, from, to) => {
// Update task order when items are moved
const newTasks = [...tasks];
const [movedTask] = newTasks.splice(from, 1);
newTasks.splice(to, 0, movedTask);
onTaskReorder(newTasks);
}}
>
<TaskCard task={item} />
</SortableItem>
), [tasks, onTaskReorder]);
return (
<Sortable
data={tasks}
renderItem={renderTask}
itemHeight={60}
/>
);
}
Key Features:
- Automatic list reordering
- Smooth item animations
- Handle-based sorting
- Custom item heights
- Performance optimization for large lists
DropProvider
The DropProvider
creates a context that enables:
- Component Registration: Draggable and droppable components register themselves
- Collision Detection: Automatic detection of overlaps between components
- State Management: Tracking of active drags and dropped items
- Position Updates: Handling layout changes and position recalculations
- Event Coordination: Global callbacks for drag events
Provider Configuration
import { GestureHandlerRootView } from 'react-native-gesture-handler';
<GestureHandlerRootView style={{ flex: 1 }}>
<DropProvider
onDragStart={(data) => console.log('Drag started:', data)}
onDragEnd={(data) => console.log('Drag ended:', data)}
onDroppedItemsUpdate={(items) => console.log('Items updated:', items)}
onDragging={({ x, y, tx, ty, itemData }) => {
console.log(`${itemData.name} at (${x + tx}, ${y + ty})`);
}}
>
<YourApp />
</DropProvider>
</GestureHandlerRootView>
Data Flow
1. Registration Phase
When components mount, they register with the DropProvider:
// Draggable registers itself with unique ID and data
<Draggable data={{ id: '1', name: 'Item 1' }}>
{/* Component registers on mount */}
</Draggable>
// Droppable registers itself with position and drop handler
<Droppable onDrop={handleDrop}>
{/* Component registers on mount */}
</Droppable>
2. Drag Initiation
When a drag starts:
- Gesture system detects pan gesture
- Draggable component becomes active
- Position tracking begins
- Global
onDragStart
callback fires
3. Collision Detection
During dragging:
- Real-time position updates
- Collision algorithm checks overlaps
- Visual feedback for valid drop zones
- Global
onDragging
callback fires
4. Drop Resolution
When drag ends:
- Check for valid drop target
- Execute drop handler if valid
- Animate item to final position
- Update dropped items registry
- Global
onDragEnd
callback fires
State Management
Internal State
The DropProvider manages several pieces of internal state:
- Active Hover Slot: Currently hovered drop zone
- Dropped Items Map: Registry of successfully dropped items
- Position Update Listeners: Components that need position updates
External State Integration
Integrate with your app's state management:
function App() {
const [tasks, setTasks] = useState(initialTasks);
const [droppedItems, setDroppedItems] = useState({});
const handleDroppedItemsUpdate = (items) => {
setDroppedItems(items);
// Sync with your state management
const updatedTasks = syncTasksWithDroppedItems(tasks, items);
setTasks(updatedTasks);
// Persist to backend
saveTasks(updatedTasks);
};
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<DropProvider onDroppedItemsUpdate={handleDroppedItemsUpdate}>
<TaskBoard tasks={tasks} />
</DropProvider>
</GestureHandlerRootView>
);
}
Performance Considerations
Automatic Optimizations
The library includes several automatic optimizations:
- Memoized Context Values: Prevents unnecessary re-renders
- Throttled Position Updates: Maintains 60fps during drags
- Efficient Collision Detection: Optimized algorithms for different use cases
- Automatic Cleanup: Components clean up on unmount
Manual Optimizations
You can further optimize performance:
// Use React.memo for expensive components
const ExpensiveTaskCard = React.memo(({ task }) => {
return (
<Draggable data={task}>
<ComplexTaskVisualization task={task} />
</Draggable>
);
});
// Use useCallback for event handlers
const handleDrop = useCallback((data) => {
updateTasks(data);
}, []);
// Throttle expensive operations in drag callbacks
const handleDragging = useCallback(
throttle(({ itemData, tx, ty }) => {
updateExpensiveVisualization(itemData, tx, ty);
}, 16), // 60fps
[]
);
Error Handling
Built-in Error Handling
The library includes automatic error handling:
- Context validation (components must be within DropProvider)
- Type checking with TypeScript
- Graceful degradation for missing props
- Development-time warnings for common mistakes
Custom Error Handling
Add your own error boundaries:
function DragDropErrorBoundary({ children }) {
return (
<ErrorBoundary
fallback={<Text>Drag and drop temporarily unavailable</Text>}
onError={(error) => {
console.error('Drag and drop error:', error);
analytics.track('dnd_error', { error: error.message });
}}
>
{children}
</ErrorBoundary>
);
}
function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<DragDropErrorBoundary>
<DropProvider>
<YourApp />
</DropProvider>
</DragDropErrorBoundary>
</GestureHandlerRootView>
);
}
TypeScript Support
The library is fully typed with comprehensive TypeScript support:
interface TaskData {
id: string;
title: string;
priority: 'low' | 'medium' | 'high';
}
// Typed draggable
<Draggable<TaskData> data={task}>
<TaskCard task={task} />
</Draggable>
// Typed droppable
<Droppable<TaskData> onDrop={(data: TaskData) => {
// data is properly typed
console.log(`Dropped: ${data.title}`);
}}>
<DropZone />
</Droppable>
// Typed provider callbacks
<DropProvider
onDragStart={(data: TaskData) => {
console.log(`Started dragging: ${data.title}`);
}}
onDroppedItemsUpdate={(items: DroppedItemsMap<TaskData>) => {
// items is properly typed
updateTaskState(items);
}}
>
<TaskBoard />
</DropProvider>
Next Steps
Now that you understand the basic concepts:
- Quick Start - Build your first drag-and-drop interface
- Setup Provider - Learn advanced provider configuration
- API Reference - Explore all available components and hooks
- Examples - See real-world implementations
- Performance Guide - Optimize your implementation