Performance
Tips and patterns for keeping drag-and-drop interactions smooth at 60fps.
Architecture Advantages
React Native Reanimated DnD is built on Reanimated 4 and Gesture Handler, which means:
- Gesture recognition runs on the UI thread — no JS bridge round-trips during a drag
- Animations run on the UI thread — position updates don't wait for JavaScript
- Collision detection is lightweight — simple rectangle math, no complex geometry
This architecture gives you 60fps performance by default. The tips below help you avoid common pitfalls that can degrade it.
Avoid Re-renders During Drag
The biggest performance risk is triggering React re-renders while a drag is in progress.
Use scheduleOnRN Sparingly
The onDragging callback fires on every drag movement frame. If your callback triggers state updates, each update causes a re-render:
// Bad: State update on every frame
<Draggable
data={data}
onDragging={({ tx, ty }) => {
setPosition({ x: tx, y: ty }); // Re-render every frame!
}}
>// Good: Only update when necessary
<Draggable
data={data}
onDragging={({ tx, ty }) => {
// Throttle updates or use shared values instead
if (Math.abs(tx - lastTx.current) > 10) {
lastTx.current = tx;
// Update only when significant movement occurs
}
}}
>Use Shared Values for Real-time Data
If you need to track drag position in real-time, use Reanimated shared values instead of React state:
const dragX = useSharedValue(0);
const dragY = useSharedValue(0);
<Draggable
data={data}
onDragging={({ tx, ty }) => {
dragX.value = tx;
dragY.value = ty;
}}
>Memoize Callbacks and Data
Stable data Objects
The data prop is passed through the drag system. Create it outside the render or memoize it:
// Bad: New object every render
<Draggable data={{ id: "1", name: "Task" }}>
// Good: Stable reference
const data = useMemo(() => ({ id: "1", name: "Task" }), []);
<Draggable data={data}>Stable Callbacks
// Memoize event handlers
const handleDrop = useCallback((data) => {
processItem(data);
}, []);
<Droppable onDrop={handleDrop}>Stable Animation Functions
// Define outside component or memoize
const springAnimation = (toValue: number) => {
"worklet";
return withSpring(toValue, { damping: 15 });
};
// Or inside component with useCallback
const animation = useCallback((toValue: number) => {
"worklet";
return withSpring(toValue, { damping: 15 });
}, []);Sortable List Performance
Use the Built-in Sortable Component
Sortable and SortableItem are optimized for list reordering. They handle position calculations on the UI thread and minimize JS bridge traffic.
FlatList Virtualization
For long lists, Sortable supports FlatList-style virtualization out of the box. Only visible items are rendered:
<Sortable
data={items} // Can be hundreds of items
renderItem={({ item, id, ...props }) => (
<SortableItem key={id} id={id} data={item} {...props}>
<TaskRow task={item} />
</SortableItem>
)}
itemHeight={60}
/>Keep Item Components Light
Heavy item components slow down reordering animations:
// Avoid: Complex rendering in each item
<SortableItem ...>
<ExpensiveChart data={item.chartData} />
<ComplexForm fields={item.fields} />
</SortableItem>
// Better: Simple presentation
<SortableItem ...>
<View style={styles.row}>
<Text>{item.title}</Text>
<Text>{item.subtitle}</Text>
</View>
</SortableItem>Grid Performance
Set Correct Dimensions
SortableGrid uses absolute positioning. Providing accurate dimensions avoids layout thrashing:
<SortableGrid
data={items}
dimensions={{
columns: 4,
itemWidth: 80,
itemHeight: 80,
rowGap: 12,
columnGap: 12,
}}
renderItem={...}
/>Limit Grid Size
Grids with many items (50+) may experience slower reorder calculations. For very large grids, consider pagination or virtualization.
Collision Algorithm Performance
The three algorithms have slightly different costs:
intersect— Fastest. Four comparisons (AABB overlap).center— Fast. Two comparisons (point-in-rectangle).contain— Slightly slower. Four comparisons but all must pass.
In practice the difference is negligible, but if you have many droppable zones (20+) and need maximum performance, intersect is the safest choice.
Drag Handles
Use drag handles for large or complex draggable components. When a handle is present, only the handle area responds to gestures — this reduces the gesture recognition surface and avoids conflicts with scrollable content inside the draggable:
<Draggable data={data}>
<View style={styles.card}>
<Draggable.Handle>
<View style={styles.handle}>
<Text>⠿</Text>
</View>
</Draggable.Handle>
<ScrollView>
{/* Complex scrollable content */}
</ScrollView>
</View>
</Draggable>General Tips
-
Profile on a real device — Simulators don't accurately represent gesture and animation performance.
-
Use Reanimated's
useAnimatedReactioninstead ofuseEffectfor responding to shared value changes — it stays on the UI thread. -
Avoid
console.login drag callbacks — Logging during drag can cause frame drops. Use it only for debugging. -
Keep the component tree shallow inside draggable items — fewer nested Views means faster layout calculations.
-
Test with production builds — Development mode includes extra checks that slow down rendering. Always benchmark with
--releaseor production builds.
See Also
- Sortable Component — Optimized list reordering
- SortableGrid Component — Optimized grid reordering
- React Native Reanimated Performance — Reanimated performance docs